@fivenorth/loop-sdk 0.7.3 → 0.7.5

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 +245 -128
  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,84 +2692,9 @@ 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
- version = "0.7.3";
2697
+ version = "0.7.5";
2698
2698
  appName = "Unknown";
2699
2699
  connection = null;
2700
2700
  session = null;
@@ -2789,28 +2789,33 @@ 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) {
2793
- return;
2792
+ if (!this.session) {
2793
+ throw new Error("No valid session found. The network connection maynot available or the backend is not reachable.");
2794
2794
  }
2795
- if (this.session && this.session.isAuthorized()) {
2795
+ if (this.session.isAuthorized()) {
2796
2796
  return;
2797
2797
  }
2798
2798
  try {
2799
- const { ticket_id: ticketId } = await this.connection.getTicket(this.appName, this.session.sessionId, this.version);
2800
- this.session.setTicketId(ticketId);
2801
- const connectUrl = this.buildConnectUrl(ticketId);
2802
- this.showQrCode(connectUrl);
2803
- this.connection.connectWebSocket(ticketId, this.handleWebSocketMessage.bind(this));
2799
+ if (!this.session.ticketId) {
2800
+ const { ticket_id: ticketId } = await this.connection.getTicket(this.appName, this.session.sessionId, this.version);
2801
+ this.session.setTicketId(ticketId);
2802
+ }
2803
+ if (!this.connection.connectInProgress()) {
2804
+ this.connection.connectWebSocket(this.session.ticketId, this.handleWebSocketMessage.bind(this));
2805
+ }
2806
+ this.showQrCode(this.buildConnectUrl(this.session.ticketId));
2804
2807
  } catch (error) {
2805
2808
  console.error(error);
2806
- return;
2809
+ throw error;
2807
2810
  }
2808
2811
  }
2809
2812
  handleWebSocketMessage(event) {
2810
2813
  const message = JSON.parse(event.data);
2811
2814
  const errCode = extractErrorCode(message);
2812
2815
  if (isUnauthCode(errCode)) {
2813
- console.warn("[LoopSDK] Detected session invalidation:", errCode, { message });
2816
+ console.warn("[LoopSDK] Detected session invalidation:", errCode, {
2817
+ message
2818
+ });
2814
2819
  this.logout();
2815
2820
  return;
2816
2821
  }
@@ -2916,7 +2921,108 @@ class LoopSDK {
2916
2921
  }
2917
2922
  return window.open(url, "_blank", "noopener,noreferrer");
2918
2923
  }
2924
+ injectModalStyles() {
2925
+ if (document.getElementById("loop-connect-styles"))
2926
+ return;
2927
+ const style = document.createElement("style");
2928
+ style.id = "loop-connect-styles";
2929
+ style.textContent = `
2930
+ .loop-connect {
2931
+ position: fixed;
2932
+ inset: 0;
2933
+ background: oklch(0.222 0 0 / 0.85);
2934
+ backdrop-filter: blur(8px);
2935
+ display: flex;
2936
+ justify-content: center;
2937
+ align-items: center;
2938
+ z-index: 10000;
2939
+ font-family: system-ui, -apple-system, sans-serif;
2940
+ animation: fadeIn 0.2s ease-out;
2941
+ }
2942
+ .loop-connect dialog {
2943
+ position: relative;
2944
+ overflow: hidden;
2945
+ background: oklch(0.253 0.008 274.6);
2946
+ box-shadow: 0 4px 24px oklch(0 0 0 / 0.1);
2947
+ border: 1px solid oklch(0.41 0.01 278.4);
2948
+ border-radius: 32px;
2949
+ padding: 24px;
2950
+ display: flex;
2951
+ flex-direction: column;
2952
+ align-items: center;
2953
+ gap: 16px;
2954
+ color: oklch(0.975 0.005 280);
2955
+ }
2956
+ .loop-connect .bg-logo {
2957
+ position: absolute;
2958
+ right: -20px;
2959
+ top: -40px;
2960
+ width: 140px;
2961
+ height: auto;
2962
+ opacity: 0.06;
2963
+ pointer-events: none;
2964
+ }
2965
+ .loop-connect h3 {
2966
+ margin: 0;
2967
+ font-size: 18px;
2968
+ font-weight: 600;
2969
+ letter-spacing: -0.015em;
2970
+ }
2971
+ .loop-connect figure {
2972
+ margin: 0;
2973
+ background: oklch(1 0 0);
2974
+ padding: 8px;
2975
+ border-radius: 24px;
2976
+ display: flex;
2977
+ justify-content: center;
2978
+ border: 2px solid oklch(0.41 0.01 278.4);
2979
+ box-shadow: 0 4px 24px oklch(0 0 0 / 0.1);
2980
+ }
2981
+ .loop-connect img {
2982
+ display: block;
2983
+ width: 225px;
2984
+ height: 225px;
2985
+ }
2986
+ .loop-connect .divider {
2987
+ width: 100%;
2988
+ display: flex;
2989
+ align-items: center;
2990
+ gap: 16px;
2991
+ color: oklch(0.554 0.012 280.3);
2992
+ font-size: 13px;
2993
+ font-weight: 600;
2994
+ }
2995
+ .loop-connect .divider::before,
2996
+ .loop-connect .divider::after {
2997
+ content: "";
2998
+ flex: 1;
2999
+ height: 1px;
3000
+ background: oklch(0.45 0.01 278);
3001
+ }
3002
+ .loop-connect button {
3003
+ background: oklch(0.976 0.101 112.3);
3004
+ border: 1px solid oklch(0.82 0.16 110);
3005
+ color: oklch(0.222 0 0);
3006
+ padding: 16px 32px;
3007
+ border-radius: 24px;
3008
+ font-size: 15px;
3009
+ font-weight: 600;
3010
+ cursor: pointer;
3011
+ transition: all 0.2s ease;
3012
+ width: 100%;
3013
+ }
3014
+ .loop-connect button:hover {
3015
+ background: oklch(0.98 0.105 112.5);
3016
+ }
3017
+ @keyframes fadeIn {
3018
+ from { opacity: 0; }
3019
+ to { opacity: 1; }
3020
+ }
3021
+ `;
3022
+ document.head.appendChild(style);
3023
+ }
2919
3024
  showQrCode(url) {
3025
+ this.injectModalStyles();
2920
3026
  import_qrcode.default.toDataURL(url, (err, dataUrl) => {
2921
3027
  if (err) {
2922
3028
  console.error("Failed to generate QR code", err);
@@ -2924,42 +3030,53 @@ class LoopSDK {
2924
3030
  }
2925
3031
  const overlay = document.createElement("div");
2926
3032
  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";
3033
+ overlay.className = "loop-sdk-connect-overlay loop-connect";
3034
+ const dialog = document.createElement("dialog");
3035
+ dialog.open = true;
3036
+ const bgLogo = document.createElementNS("http://www.w3.org/2000/svg", "svg");
3037
+ bgLogo.setAttribute("class", "bg-logo");
3038
+ bgLogo.setAttribute("viewBox", "0 0 124.05 305.64");
3039
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
3040
+ path.setAttribute("d", "M24.58,99.47L124.05,0v224.42L24.58,124.95c-7.04-7.04-7.04-18.45,0-25.49Z");
3041
+ path.setAttribute("fill", "currentColor");
3042
+ const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
3043
+ rect.setAttribute("x", "12.89");
3044
+ rect.setAttribute("y", "194.48");
3045
+ rect.setAttribute("width", "98.27");
3046
+ rect.setAttribute("height", "98.27");
3047
+ rect.setAttribute("rx", "18.02");
3048
+ rect.setAttribute("ry", "18.02");
3049
+ rect.setAttribute("transform", "translate(-154.1 115.21) rotate(-45)");
3050
+ rect.setAttribute("fill", "currentColor");
3051
+ bgLogo.appendChild(path);
3052
+ bgLogo.appendChild(rect);
3053
+ const title = document.createElement("h3");
3054
+ title.textContent = "Scan with Phone";
3055
+ const figure = document.createElement("figure");
2944
3056
  const img = document.createElement("img");
2945
3057
  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();
3058
+ img.alt = "QR Code";
3059
+ figure.appendChild(img);
3060
+ const divider = document.createElement("div");
3061
+ divider.className = "divider";
3062
+ divider.textContent = "OR";
3063
+ const button = document.createElement("button");
3064
+ button.type = "button";
3065
+ button.textContent = "Continue in Browser";
3066
+ button.addEventListener("click", () => {
2954
3067
  this.openWallet(url);
2955
- };
2956
- content.appendChild(link);
2957
- overlay.appendChild(content);
2958
- overlay.onclick = (e) => {
3068
+ });
3069
+ dialog.appendChild(bgLogo);
3070
+ dialog.appendChild(title);
3071
+ dialog.appendChild(figure);
3072
+ dialog.appendChild(divider);
3073
+ dialog.appendChild(button);
3074
+ overlay.appendChild(dialog);
3075
+ overlay.addEventListener("click", (e) => {
2959
3076
  if (e.target === overlay) {
2960
3077
  this.hideQrCode();
2961
3078
  }
2962
- };
3079
+ });
2963
3080
  document.body.appendChild(overlay);
2964
3081
  this.overlay = overlay;
2965
3082
  });
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.5",
4
+ "author": "support@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
  }