@fivenorth/loop-sdk 0.7.3 → 0.7.4

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.
Files changed (2) hide show
  1. package/dist/index.js +235 -120
  2. package/package.json +29 -29
package/dist/index.js CHANGED
@@ -2055,18 +2055,6 @@ var require_browser = __commonJS((exports) => {
2055
2055
  // src/index.ts
2056
2056
  var import_qrcode = __toESM(require_browser(), 1);
2057
2057
 
2058
- // src/types.ts
2059
- var MessageType;
2060
- ((MessageType2) => {
2061
- MessageType2["HANDSHAKE_ACCEPT"] = "handshake_accept";
2062
- MessageType2["HANDSHAKE_REJECT"] = "handshake_reject";
2063
- MessageType2["RUN_TRANSACTION"] = "run_transaction";
2064
- MessageType2["RUN_TRANSACTION_RESPONSE"] = "run_transaction_response";
2065
- MessageType2["SIGN_RAW_MESSAGE"] = "sign_raw_message";
2066
- MessageType2["SIGN_RAW_MESSAGE_RESPONSE"] = "sign_raw_message_response";
2067
- MessageType2["REJECT_REQUEST"] = "reject_request";
2068
- })(MessageType ||= {});
2069
-
2070
2058
  // src/errors.ts
2071
2059
  class RequestTimeoutError extends Error {
2072
2060
  constructor(timeout) {
@@ -2327,6 +2315,18 @@ class Connection {
2327
2315
  }
2328
2316
  }
2329
2317
 
2318
+ // src/types.ts
2319
+ var MessageType;
2320
+ ((MessageType2) => {
2321
+ MessageType2["HANDSHAKE_ACCEPT"] = "handshake_accept";
2322
+ MessageType2["HANDSHAKE_REJECT"] = "handshake_reject";
2323
+ MessageType2["RUN_TRANSACTION"] = "run_transaction";
2324
+ MessageType2["RUN_TRANSACTION_RESPONSE"] = "run_transaction_response";
2325
+ MessageType2["SIGN_RAW_MESSAGE"] = "sign_raw_message";
2326
+ MessageType2["SIGN_RAW_MESSAGE_RESPONSE"] = "sign_raw_message_response";
2327
+ MessageType2["REJECT_REQUEST"] = "reject_request";
2328
+ })(MessageType ||= {});
2329
+
2330
2330
  // src/provider.ts
2331
2331
  var DEFAULT_REQUEST_TIMEOUT_MS = 300000;
2332
2332
  function generateUUID() {
@@ -2540,6 +2540,81 @@ class Provider {
2540
2540
  }
2541
2541
  }
2542
2542
 
2543
+ // src/session.ts
2544
+ var STORAGE_KEY_LOOP_CONNECT = "loop_connect";
2545
+
2546
+ class SessionInfo {
2547
+ sessionId;
2548
+ ticketId;
2549
+ authToken;
2550
+ partyId;
2551
+ publicKey;
2552
+ email;
2553
+ _isAuthorized = false;
2554
+ constructor({ sessionId, ticketId, authToken, partyId, publicKey, email }) {
2555
+ this.sessionId = sessionId;
2556
+ this.ticketId = ticketId;
2557
+ this.authToken = authToken;
2558
+ this.partyId = partyId;
2559
+ this.publicKey = publicKey;
2560
+ this.email = email;
2561
+ }
2562
+ setTicketId(ticketId) {
2563
+ this.ticketId = ticketId;
2564
+ this.save();
2565
+ }
2566
+ authorized() {
2567
+ if (this.ticketId === undefined || this.sessionId === undefined || this.authToken === undefined || this.partyId === undefined || this.publicKey === undefined) {
2568
+ throw new Error("Session cannot be authorized without all required fields.");
2569
+ }
2570
+ this._isAuthorized = true;
2571
+ }
2572
+ isPreAuthorized() {
2573
+ return !this._isAuthorized && this.ticketId !== undefined && this.sessionId !== undefined && this.authToken !== undefined && this.partyId !== undefined && this.publicKey !== undefined;
2574
+ }
2575
+ isAuthorized() {
2576
+ return this._isAuthorized;
2577
+ }
2578
+ save() {
2579
+ localStorage.setItem("loop_connect", this.toJson());
2580
+ }
2581
+ reset() {
2582
+ localStorage.removeItem(STORAGE_KEY_LOOP_CONNECT);
2583
+ this.sessionId = generateRequestId();
2584
+ this._isAuthorized = false;
2585
+ this.ticketId = undefined;
2586
+ this.authToken = undefined;
2587
+ this.partyId = undefined;
2588
+ this.publicKey = undefined;
2589
+ this.email = undefined;
2590
+ }
2591
+ static fromStorage() {
2592
+ const existingConnectionRaw = localStorage.getItem(STORAGE_KEY_LOOP_CONNECT);
2593
+ if (!existingConnectionRaw) {
2594
+ return new SessionInfo({ sessionId: generateRequestId() });
2595
+ }
2596
+ let session = null;
2597
+ try {
2598
+ session = new SessionInfo(JSON.parse(existingConnectionRaw));
2599
+ } catch (error) {
2600
+ console.error("Failed to parse existing connection info, local storage is corrupted.", error);
2601
+ localStorage.removeItem(STORAGE_KEY_LOOP_CONNECT);
2602
+ session = new SessionInfo({ sessionId: generateRequestId() });
2603
+ }
2604
+ return session;
2605
+ }
2606
+ toJson() {
2607
+ return JSON.stringify({
2608
+ sessionId: this.sessionId,
2609
+ ticketId: this.ticketId,
2610
+ authToken: this.authToken,
2611
+ partyId: this.partyId,
2612
+ publicKey: this.publicKey,
2613
+ email: this.email
2614
+ });
2615
+ }
2616
+ }
2617
+
2543
2618
  // src/extensions/usdc/index.ts
2544
2619
  class UsdcBridge {
2545
2620
  getProvider;
@@ -2617,81 +2692,6 @@ class LoopWallet {
2617
2692
  }
2618
2693
  }
2619
2694
 
2620
- // src/session.ts
2621
- var STORAGE_KEY_LOOP_CONNECT = "loop_connect";
2622
-
2623
- class SessionInfo {
2624
- sessionId;
2625
- ticketId;
2626
- authToken;
2627
- partyId;
2628
- publicKey;
2629
- email;
2630
- _isAuthorized = false;
2631
- constructor({ sessionId, ticketId, authToken, partyId, publicKey, email }) {
2632
- this.sessionId = sessionId;
2633
- this.ticketId = ticketId;
2634
- this.authToken = authToken;
2635
- this.partyId = partyId;
2636
- this.publicKey = publicKey;
2637
- this.email = email;
2638
- }
2639
- setTicketId(ticketId) {
2640
- this.ticketId = ticketId;
2641
- this.save();
2642
- }
2643
- authorized() {
2644
- if (this.ticketId === undefined || this.sessionId === undefined || this.authToken === undefined || this.partyId === undefined || this.publicKey === undefined) {
2645
- throw new Error("Session cannot be authorized without all required fields.");
2646
- }
2647
- this._isAuthorized = true;
2648
- }
2649
- isPreAuthorized() {
2650
- return !this._isAuthorized && this.ticketId !== undefined && this.sessionId !== undefined && this.authToken !== undefined && this.partyId !== undefined && this.publicKey !== undefined;
2651
- }
2652
- isAuthorized() {
2653
- return this._isAuthorized;
2654
- }
2655
- save() {
2656
- localStorage.setItem("loop_connect", this.toJson());
2657
- }
2658
- reset() {
2659
- localStorage.removeItem(STORAGE_KEY_LOOP_CONNECT);
2660
- this.sessionId = generateRequestId();
2661
- this._isAuthorized = false;
2662
- this.ticketId = undefined;
2663
- this.authToken = undefined;
2664
- this.partyId = undefined;
2665
- this.publicKey = undefined;
2666
- this.email = undefined;
2667
- }
2668
- static fromStorage() {
2669
- const existingConnectionRaw = localStorage.getItem(STORAGE_KEY_LOOP_CONNECT);
2670
- if (!existingConnectionRaw) {
2671
- return new SessionInfo({ sessionId: generateRequestId() });
2672
- }
2673
- let session = null;
2674
- try {
2675
- session = new SessionInfo(JSON.parse(existingConnectionRaw));
2676
- } catch (error) {
2677
- console.error("Failed to parse existing connection info, local storage is corrupted.", error);
2678
- localStorage.removeItem(STORAGE_KEY_LOOP_CONNECT);
2679
- session = new SessionInfo({ sessionId: generateRequestId() });
2680
- }
2681
- return session;
2682
- }
2683
- toJson() {
2684
- return JSON.stringify({
2685
- sessionId: this.sessionId,
2686
- ticketId: this.ticketId,
2687
- authToken: this.authToken,
2688
- partyId: this.partyId,
2689
- publicKey: this.publicKey,
2690
- email: this.email
2691
- });
2692
- }
2693
- }
2694
-
2695
2695
  // src/index.ts
2696
2696
  class LoopSDK {
2697
2697
  version = "0.7.3";
@@ -2789,7 +2789,8 @@ class LoopSDK {
2789
2789
  throw new Error("SDK not initialized. Call init() first.");
2790
2790
  }
2791
2791
  await this.autoConnect();
2792
- if (this.connection?.connectInProgress() === true) {
2792
+ if (this.session?.ticketId) {
2793
+ this.showQrCode(this.buildConnectUrl(this.session.ticketId));
2793
2794
  return;
2794
2795
  }
2795
2796
  if (this.session && this.session.isAuthorized()) {
@@ -2798,7 +2799,7 @@ class LoopSDK {
2798
2799
  try {
2799
2800
  const { ticket_id: ticketId } = await this.connection.getTicket(this.appName, this.session.sessionId, this.version);
2800
2801
  this.session.setTicketId(ticketId);
2801
- const connectUrl = this.buildConnectUrl(ticketId);
2802
+ const connectUrl = this.buildConnectUrl(this.session.ticketId);
2802
2803
  this.showQrCode(connectUrl);
2803
2804
  this.connection.connectWebSocket(ticketId, this.handleWebSocketMessage.bind(this));
2804
2805
  } catch (error) {
@@ -2810,7 +2811,9 @@ class LoopSDK {
2810
2811
  const message = JSON.parse(event.data);
2811
2812
  const errCode = extractErrorCode(message);
2812
2813
  if (isUnauthCode(errCode)) {
2813
- console.warn("[LoopSDK] Detected session invalidation:", errCode, { message });
2814
+ console.warn("[LoopSDK] Detected session invalidation:", errCode, {
2815
+ message
2816
+ });
2814
2817
  this.logout();
2815
2818
  return;
2816
2819
  }
@@ -2916,7 +2919,108 @@ class LoopSDK {
2916
2919
  }
2917
2920
  return window.open(url, "_blank", "noopener,noreferrer");
2918
2921
  }
2922
+ injectModalStyles() {
2923
+ if (document.getElementById("loop-connect-styles"))
2924
+ return;
2925
+ const style = document.createElement("style");
2926
+ style.id = "loop-connect-styles";
2927
+ style.textContent = `
2928
+ .loop-connect {
2929
+ position: fixed;
2930
+ inset: 0;
2931
+ background: oklch(0.222 0 0 / 0.85);
2932
+ backdrop-filter: blur(8px);
2933
+ display: flex;
2934
+ justify-content: center;
2935
+ align-items: center;
2936
+ z-index: 10000;
2937
+ font-family: system-ui, -apple-system, sans-serif;
2938
+ animation: fadeIn 0.2s ease-out;
2939
+ }
2940
+ .loop-connect dialog {
2941
+ position: relative;
2942
+ overflow: hidden;
2943
+ background: oklch(0.253 0.008 274.6);
2944
+ box-shadow: 0 4px 24px oklch(0 0 0 / 0.1);
2945
+ border: 1px solid oklch(0.41 0.01 278.4);
2946
+ border-radius: 32px;
2947
+ padding: 24px;
2948
+ display: flex;
2949
+ flex-direction: column;
2950
+ align-items: center;
2951
+ gap: 16px;
2952
+ color: oklch(0.975 0.005 280);
2953
+ }
2954
+ .loop-connect .bg-logo {
2955
+ position: absolute;
2956
+ right: -20px;
2957
+ top: -40px;
2958
+ width: 140px;
2959
+ height: auto;
2960
+ opacity: 0.06;
2961
+ pointer-events: none;
2962
+ }
2963
+ .loop-connect h3 {
2964
+ margin: 0;
2965
+ font-size: 18px;
2966
+ font-weight: 600;
2967
+ letter-spacing: -0.015em;
2968
+ }
2969
+ .loop-connect figure {
2970
+ margin: 0;
2971
+ background: oklch(1 0 0);
2972
+ padding: 8px;
2973
+ border-radius: 24px;
2974
+ display: flex;
2975
+ justify-content: center;
2976
+ border: 2px solid oklch(0.41 0.01 278.4);
2977
+ box-shadow: 0 4px 24px oklch(0 0 0 / 0.1);
2978
+ }
2979
+ .loop-connect img {
2980
+ display: block;
2981
+ width: 225px;
2982
+ height: 225px;
2983
+ }
2984
+ .loop-connect .divider {
2985
+ width: 100%;
2986
+ display: flex;
2987
+ align-items: center;
2988
+ gap: 16px;
2989
+ color: oklch(0.554 0.012 280.3);
2990
+ font-size: 13px;
2991
+ font-weight: 600;
2992
+ }
2993
+ .loop-connect .divider::before,
2994
+ .loop-connect .divider::after {
2995
+ content: "";
2996
+ flex: 1;
2997
+ height: 1px;
2998
+ background: oklch(0.45 0.01 278);
2999
+ }
3000
+ .loop-connect button {
3001
+ background: oklch(0.976 0.101 112.3);
3002
+ border: 1px solid oklch(0.82 0.16 110);
3003
+ color: oklch(0.222 0 0);
3004
+ padding: 16px 32px;
3005
+ border-radius: 24px;
3006
+ font-size: 15px;
3007
+ font-weight: 600;
3008
+ cursor: pointer;
3009
+ transition: all 0.2s ease;
3010
+ width: 100%;
3011
+ }
3012
+ .loop-connect button:hover {
3013
+ background: oklch(0.98 0.105 112.5);
3014
+ }
3015
+ @keyframes fadeIn {
3016
+ from { opacity: 0; }
3017
+ to { opacity: 1; }
3018
+ }
3019
+ `;
3020
+ document.head.appendChild(style);
3021
+ }
2919
3022
  showQrCode(url) {
3023
+ this.injectModalStyles();
2920
3024
  import_qrcode.default.toDataURL(url, (err, dataUrl) => {
2921
3025
  if (err) {
2922
3026
  console.error("Failed to generate QR code", err);
@@ -2924,42 +3028,53 @@ class LoopSDK {
2924
3028
  }
2925
3029
  const overlay = document.createElement("div");
2926
3030
  overlay.id = "loop-sdk-connect-overlay";
2927
- overlay.className = "loop-sdk-connect-overlay";
2928
- overlay.style.position = "fixed";
2929
- overlay.style.top = "0";
2930
- overlay.style.left = "0";
2931
- overlay.style.width = "100%";
2932
- overlay.style.height = "100%";
2933
- overlay.style.backgroundColor = "rgba(0,0,0,0.9)";
2934
- overlay.style.display = "flex";
2935
- overlay.style.justifyContent = "center";
2936
- overlay.style.alignItems = "center";
2937
- overlay.style.zIndex = "1000";
2938
- overlay.style.flexDirection = "column";
2939
- const content = document.createElement("div");
2940
- content.className = "loop-sdk-connect-content";
2941
- content.style.display = "flex";
2942
- content.style.flexDirection = "column";
2943
- content.style.alignItems = "center";
3031
+ overlay.className = "loop-sdk-connect-overlay loop-connect";
3032
+ const dialog = document.createElement("dialog");
3033
+ dialog.open = true;
3034
+ const bgLogo = document.createElementNS("http://www.w3.org/2000/svg", "svg");
3035
+ bgLogo.setAttribute("class", "bg-logo");
3036
+ bgLogo.setAttribute("viewBox", "0 0 124.05 305.64");
3037
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
3038
+ path.setAttribute("d", "M24.58,99.47L124.05,0v224.42L24.58,124.95c-7.04-7.04-7.04-18.45,0-25.49Z");
3039
+ path.setAttribute("fill", "currentColor");
3040
+ const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
3041
+ rect.setAttribute("x", "12.89");
3042
+ rect.setAttribute("y", "194.48");
3043
+ rect.setAttribute("width", "98.27");
3044
+ rect.setAttribute("height", "98.27");
3045
+ rect.setAttribute("rx", "18.02");
3046
+ rect.setAttribute("ry", "18.02");
3047
+ rect.setAttribute("transform", "translate(-154.1 115.21) rotate(-45)");
3048
+ rect.setAttribute("fill", "currentColor");
3049
+ bgLogo.appendChild(path);
3050
+ bgLogo.appendChild(rect);
3051
+ const title = document.createElement("h3");
3052
+ title.textContent = "Scan with Phone";
3053
+ const figure = document.createElement("figure");
2944
3054
  const img = document.createElement("img");
2945
3055
  img.src = dataUrl;
2946
- content.appendChild(img);
2947
- const link = document.createElement("a");
2948
- link.href = url;
2949
- link.textContent = "Or click here to connect";
2950
- link.style.color = "white";
2951
- link.style.marginTop = "20px";
2952
- link.onclick = (e) => {
2953
- e.preventDefault();
3056
+ img.alt = "QR Code";
3057
+ figure.appendChild(img);
3058
+ const divider = document.createElement("div");
3059
+ divider.className = "divider";
3060
+ divider.textContent = "OR";
3061
+ const button = document.createElement("button");
3062
+ button.type = "button";
3063
+ button.textContent = "Continue in Browser";
3064
+ button.addEventListener("click", () => {
2954
3065
  this.openWallet(url);
2955
- };
2956
- content.appendChild(link);
2957
- overlay.appendChild(content);
2958
- overlay.onclick = (e) => {
3066
+ });
3067
+ dialog.appendChild(bgLogo);
3068
+ dialog.appendChild(title);
3069
+ dialog.appendChild(figure);
3070
+ dialog.appendChild(divider);
3071
+ dialog.appendChild(button);
3072
+ overlay.appendChild(dialog);
3073
+ overlay.addEventListener("click", (e) => {
2959
3074
  if (e.target === overlay) {
2960
3075
  this.hideQrCode();
2961
3076
  }
2962
- };
3077
+ });
2963
3078
  document.body.appendChild(overlay);
2964
3079
  this.overlay = overlay;
2965
3080
  });
package/package.json CHANGED
@@ -1,31 +1,31 @@
1
1
  {
2
- "name": "@fivenorth/loop-sdk",
3
- "version": "0.7.3",
4
- "author": "hello@fivenorth.io",
5
- "main": "dist/index.js",
6
- "module": "dist/index.js",
7
- "devDependencies": {
8
- "@types/bun": "latest"
9
- },
10
- "peerDependencies": {
11
- "typescript": "^5"
12
- },
13
- "files": [
14
- "dist"
15
- ],
16
- "publishConfig": {
17
- "access": "public"
18
- },
19
- "repository": "github:fivenorth-io/loop-sdk",
20
- "scripts": {
21
- "build": "bun build ./src/index.ts --outdir ./dist",
22
- "prepublishOnly": "bun run build",
23
- "start": "bun run src/server.ts"
24
- },
25
- "type": "module",
26
- "types": "dist/index.d.ts",
27
- "dependencies": {
28
- "@types/qrcode": "^1.5.6",
29
- "qrcode": "^1.5.4"
30
- }
2
+ "name": "@fivenorth/loop-sdk",
3
+ "version": "0.7.4",
4
+ "author": "hello@fivenorth.io",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.js",
7
+ "devDependencies": {
8
+ "@types/bun": "latest"
9
+ },
10
+ "peerDependencies": {
11
+ "typescript": "^5"
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "repository": "github:fivenorth-io/loop-sdk",
20
+ "scripts": {
21
+ "build": "bun build ./src/index.ts --outdir ./dist",
22
+ "prepublishOnly": "bun run build",
23
+ "start": "bun run src/server.ts"
24
+ },
25
+ "type": "module",
26
+ "types": "dist/index.d.ts",
27
+ "dependencies": {
28
+ "@types/qrcode": "^1.5.6",
29
+ "qrcode": "^1.5.4"
30
+ }
31
31
  }