@sogni-ai/expo-client 1.0.0-alpha.2

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 (185) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +87 -0
  3. package/dist/cjs/Account/index.d.ts +39 -0
  4. package/dist/cjs/Account/index.d.ts.map +1 -0
  5. package/dist/cjs/Account/index.js +111 -0
  6. package/dist/cjs/Account/index.js.map +1 -0
  7. package/dist/cjs/Account/types.d.ts +25 -0
  8. package/dist/cjs/Account/types.d.ts.map +1 -0
  9. package/dist/cjs/Account/types.js +18 -0
  10. package/dist/cjs/Account/types.js.map +1 -0
  11. package/dist/cjs/ApiClient/WebSocketClient/events.d.ts +51 -0
  12. package/dist/cjs/ApiClient/WebSocketClient/events.d.ts.map +1 -0
  13. package/dist/cjs/ApiClient/WebSocketClient/events.js +3 -0
  14. package/dist/cjs/ApiClient/WebSocketClient/events.js.map +1 -0
  15. package/dist/cjs/ApiClient/WebSocketClient/index.d.ts +34 -0
  16. package/dist/cjs/ApiClient/WebSocketClient/index.d.ts.map +1 -0
  17. package/dist/cjs/ApiClient/WebSocketClient/index.js +183 -0
  18. package/dist/cjs/ApiClient/WebSocketClient/index.js.map +1 -0
  19. package/dist/cjs/ApiClient/WebSocketClient/normalize.d.ts +24 -0
  20. package/dist/cjs/ApiClient/WebSocketClient/normalize.d.ts.map +1 -0
  21. package/dist/cjs/ApiClient/WebSocketClient/normalize.js +87 -0
  22. package/dist/cjs/ApiClient/WebSocketClient/normalize.js.map +1 -0
  23. package/dist/cjs/ApiClient/WebSocketClient/types.d.ts +9 -0
  24. package/dist/cjs/ApiClient/WebSocketClient/types.d.ts.map +1 -0
  25. package/dist/cjs/ApiClient/WebSocketClient/types.js +3 -0
  26. package/dist/cjs/ApiClient/WebSocketClient/types.js.map +1 -0
  27. package/dist/cjs/ApiClient/WebSocketClient/wireTypes.d.ts +61 -0
  28. package/dist/cjs/ApiClient/WebSocketClient/wireTypes.d.ts.map +1 -0
  29. package/dist/cjs/ApiClient/WebSocketClient/wireTypes.js +11 -0
  30. package/dist/cjs/ApiClient/WebSocketClient/wireTypes.js.map +1 -0
  31. package/dist/cjs/ApiClient/index.d.ts +57 -0
  32. package/dist/cjs/ApiClient/index.d.ts.map +1 -0
  33. package/dist/cjs/ApiClient/index.js +75 -0
  34. package/dist/cjs/ApiClient/index.js.map +1 -0
  35. package/dist/cjs/index.d.ts +55 -0
  36. package/dist/cjs/index.d.ts.map +1 -0
  37. package/dist/cjs/index.js +108 -0
  38. package/dist/cjs/index.js.map +1 -0
  39. package/dist/cjs/lib/AuthManager/ApiKeyAuthManager.d.ts +15 -0
  40. package/dist/cjs/lib/AuthManager/ApiKeyAuthManager.d.ts.map +1 -0
  41. package/dist/cjs/lib/AuthManager/ApiKeyAuthManager.js +47 -0
  42. package/dist/cjs/lib/AuthManager/ApiKeyAuthManager.js.map +1 -0
  43. package/dist/cjs/lib/AuthManager/AuthManagerBase.d.ts +23 -0
  44. package/dist/cjs/lib/AuthManager/AuthManagerBase.d.ts.map +1 -0
  45. package/dist/cjs/lib/AuthManager/AuthManagerBase.js +14 -0
  46. package/dist/cjs/lib/AuthManager/AuthManagerBase.js.map +1 -0
  47. package/dist/cjs/lib/AuthManager/AuthStorage.d.ts +11 -0
  48. package/dist/cjs/lib/AuthManager/AuthStorage.d.ts.map +1 -0
  49. package/dist/cjs/lib/AuthManager/AuthStorage.js +50 -0
  50. package/dist/cjs/lib/AuthManager/AuthStorage.js.map +1 -0
  51. package/dist/cjs/lib/AuthManager/TokenAuthManager.d.ts +35 -0
  52. package/dist/cjs/lib/AuthManager/TokenAuthManager.d.ts.map +1 -0
  53. package/dist/cjs/lib/AuthManager/TokenAuthManager.js +164 -0
  54. package/dist/cjs/lib/AuthManager/TokenAuthManager.js.map +1 -0
  55. package/dist/cjs/lib/AuthManager/index.d.ts +7 -0
  56. package/dist/cjs/lib/AuthManager/index.d.ts.map +1 -0
  57. package/dist/cjs/lib/AuthManager/index.js +13 -0
  58. package/dist/cjs/lib/AuthManager/index.js.map +1 -0
  59. package/dist/cjs/lib/Cache.d.ts +12 -0
  60. package/dist/cjs/lib/Cache.d.ts.map +1 -0
  61. package/dist/cjs/lib/Cache.js +36 -0
  62. package/dist/cjs/lib/Cache.js.map +1 -0
  63. package/dist/cjs/lib/DefaultLogger.d.ts +17 -0
  64. package/dist/cjs/lib/DefaultLogger.d.ts.map +1 -0
  65. package/dist/cjs/lib/DefaultLogger.js +36 -0
  66. package/dist/cjs/lib/DefaultLogger.js.map +1 -0
  67. package/dist/cjs/lib/RestClient.d.ts +18 -0
  68. package/dist/cjs/lib/RestClient.d.ts.map +1 -0
  69. package/dist/cjs/lib/RestClient.js +67 -0
  70. package/dist/cjs/lib/RestClient.js.map +1 -0
  71. package/dist/cjs/lib/TypedEventEmitter.d.ts +16 -0
  72. package/dist/cjs/lib/TypedEventEmitter.d.ts.map +1 -0
  73. package/dist/cjs/lib/TypedEventEmitter.js +46 -0
  74. package/dist/cjs/lib/TypedEventEmitter.js.map +1 -0
  75. package/dist/cjs/lib/base64.d.ts +3 -0
  76. package/dist/cjs/lib/base64.d.ts.map +1 -0
  77. package/dist/cjs/lib/base64.js +21 -0
  78. package/dist/cjs/lib/base64.js.map +1 -0
  79. package/dist/cjs/lib/getUUID.d.ts +12 -0
  80. package/dist/cjs/lib/getUUID.d.ts.map +1 -0
  81. package/dist/cjs/lib/getUUID.js +29 -0
  82. package/dist/cjs/lib/getUUID.js.map +1 -0
  83. package/dist/cjs/lib/utils.d.ts +10 -0
  84. package/dist/cjs/lib/utils.d.ts.map +1 -0
  85. package/dist/cjs/lib/utils.js +24 -0
  86. package/dist/cjs/lib/utils.js.map +1 -0
  87. package/dist/cjs/types/ErrorData.d.ts +7 -0
  88. package/dist/cjs/types/ErrorData.d.ts.map +1 -0
  89. package/dist/cjs/types/ErrorData.js +3 -0
  90. package/dist/cjs/types/ErrorData.js.map +1 -0
  91. package/dist/cjs/types/json.d.ts +6 -0
  92. package/dist/cjs/types/json.d.ts.map +1 -0
  93. package/dist/cjs/types/json.js +3 -0
  94. package/dist/cjs/types/json.js.map +1 -0
  95. package/dist/cjs/types/token.d.ts +2 -0
  96. package/dist/cjs/types/token.d.ts.map +1 -0
  97. package/dist/cjs/types/token.js +3 -0
  98. package/dist/cjs/types/token.js.map +1 -0
  99. package/dist/cjs/version.d.ts +2 -0
  100. package/dist/cjs/version.d.ts.map +1 -0
  101. package/dist/cjs/version.js +5 -0
  102. package/dist/cjs/version.js.map +1 -0
  103. package/dist/cjs/wallet.d.ts +6 -0
  104. package/dist/cjs/wallet.d.ts.map +1 -0
  105. package/dist/cjs/wallet.js +6 -0
  106. package/dist/cjs/wallet.js.map +1 -0
  107. package/dist/esm/Account/index.js +106 -0
  108. package/dist/esm/Account/index.js.map +1 -0
  109. package/dist/esm/Account/types.js +15 -0
  110. package/dist/esm/Account/types.js.map +1 -0
  111. package/dist/esm/ApiClient/WebSocketClient/events.js +2 -0
  112. package/dist/esm/ApiClient/WebSocketClient/events.js.map +1 -0
  113. package/dist/esm/ApiClient/WebSocketClient/index.js +178 -0
  114. package/dist/esm/ApiClient/WebSocketClient/index.js.map +1 -0
  115. package/dist/esm/ApiClient/WebSocketClient/normalize.js +81 -0
  116. package/dist/esm/ApiClient/WebSocketClient/normalize.js.map +1 -0
  117. package/dist/esm/ApiClient/WebSocketClient/types.js +2 -0
  118. package/dist/esm/ApiClient/WebSocketClient/types.js.map +1 -0
  119. package/dist/esm/ApiClient/WebSocketClient/wireTypes.js +10 -0
  120. package/dist/esm/ApiClient/WebSocketClient/wireTypes.js.map +1 -0
  121. package/dist/esm/ApiClient/index.js +68 -0
  122. package/dist/esm/ApiClient/index.js.map +1 -0
  123. package/dist/esm/index.js +66 -0
  124. package/dist/esm/index.js.map +1 -0
  125. package/dist/esm/lib/AuthManager/ApiKeyAuthManager.js +42 -0
  126. package/dist/esm/lib/AuthManager/ApiKeyAuthManager.js.map +1 -0
  127. package/dist/esm/lib/AuthManager/AuthManagerBase.js +9 -0
  128. package/dist/esm/lib/AuthManager/AuthManagerBase.js.map +1 -0
  129. package/dist/esm/lib/AuthManager/AuthStorage.js +13 -0
  130. package/dist/esm/lib/AuthManager/AuthStorage.js.map +1 -0
  131. package/dist/esm/lib/AuthManager/TokenAuthManager.js +159 -0
  132. package/dist/esm/lib/AuthManager/TokenAuthManager.js.map +1 -0
  133. package/dist/esm/lib/AuthManager/index.js +5 -0
  134. package/dist/esm/lib/AuthManager/index.js.map +1 -0
  135. package/dist/esm/lib/Cache.js +33 -0
  136. package/dist/esm/lib/Cache.js.map +1 -0
  137. package/dist/esm/lib/DefaultLogger.js +32 -0
  138. package/dist/esm/lib/DefaultLogger.js.map +1 -0
  139. package/dist/esm/lib/RestClient.js +62 -0
  140. package/dist/esm/lib/RestClient.js.map +1 -0
  141. package/dist/esm/lib/TypedEventEmitter.js +44 -0
  142. package/dist/esm/lib/TypedEventEmitter.js.map +1 -0
  143. package/dist/esm/lib/base64.js +17 -0
  144. package/dist/esm/lib/base64.js.map +1 -0
  145. package/dist/esm/lib/getUUID.js +26 -0
  146. package/dist/esm/lib/getUUID.js.map +1 -0
  147. package/dist/esm/lib/utils.js +19 -0
  148. package/dist/esm/lib/utils.js.map +1 -0
  149. package/dist/esm/types/ErrorData.js +2 -0
  150. package/dist/esm/types/ErrorData.js.map +1 -0
  151. package/dist/esm/types/json.js +2 -0
  152. package/dist/esm/types/json.js.map +1 -0
  153. package/dist/esm/types/token.js +2 -0
  154. package/dist/esm/types/token.js.map +1 -0
  155. package/dist/esm/version.js +2 -0
  156. package/dist/esm/version.js.map +1 -0
  157. package/dist/esm/wallet.js +2 -0
  158. package/dist/esm/wallet.js.map +1 -0
  159. package/package.json +91 -0
  160. package/src/Account/index.ts +118 -0
  161. package/src/Account/types.ts +41 -0
  162. package/src/ApiClient/WebSocketClient/events.ts +60 -0
  163. package/src/ApiClient/WebSocketClient/index.ts +224 -0
  164. package/src/ApiClient/WebSocketClient/normalize.ts +104 -0
  165. package/src/ApiClient/WebSocketClient/types.ts +10 -0
  166. package/src/ApiClient/WebSocketClient/wireTypes.ts +63 -0
  167. package/src/ApiClient/index.ts +134 -0
  168. package/src/index.ts +141 -0
  169. package/src/lib/AuthManager/ApiKeyAuthManager.ts +52 -0
  170. package/src/lib/AuthManager/AuthManagerBase.ts +35 -0
  171. package/src/lib/AuthManager/AuthStorage.ts +21 -0
  172. package/src/lib/AuthManager/TokenAuthManager.ts +179 -0
  173. package/src/lib/AuthManager/index.ts +8 -0
  174. package/src/lib/Cache.ts +45 -0
  175. package/src/lib/DefaultLogger.ts +47 -0
  176. package/src/lib/RestClient.ts +77 -0
  177. package/src/lib/TypedEventEmitter.ts +52 -0
  178. package/src/lib/base64.ts +17 -0
  179. package/src/lib/getUUID.ts +27 -0
  180. package/src/lib/utils.ts +21 -0
  181. package/src/types/ErrorData.ts +7 -0
  182. package/src/types/json.ts +5 -0
  183. package/src/types/token.ts +1 -0
  184. package/src/version.ts +1 -0
  185. package/src/wallet.ts +6 -0
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Generate a v4 UUID using crypto.getRandomValues, returned in uppercase form.
3
+ *
4
+ * Mac workers in the Sogni Supernet use uppercase UUIDs; the SDK keeps that
5
+ * convention end-to-end so wire-level comparisons stay simple.
6
+ *
7
+ * Requires `crypto.getRandomValues` (polyfilled in Expo via
8
+ * `expo-standard-web-crypto/polyfill`). SogniClient.create() verifies the
9
+ * polyfill is present and throws a clear error otherwise.
10
+ */
11
+ export default function getUUID() {
12
+ const bytes = crypto.getRandomValues(new Uint8Array(16));
13
+ bytes[6] = (bytes[6] & 0x0f) | 0x40;
14
+ bytes[8] = (bytes[8] & 0x3f) | 0x80;
15
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
16
+ return (hex.slice(0, 8) +
17
+ '-' +
18
+ hex.slice(8, 12) +
19
+ '-' +
20
+ hex.slice(12, 16) +
21
+ '-' +
22
+ hex.slice(16, 20) +
23
+ '-' +
24
+ hex.slice(20)).toUpperCase();
25
+ }
26
+ //# sourceMappingURL=getUUID.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getUUID.js","sourceRoot":"","sources":["../../../src/lib/getUUID.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,CAAC,OAAO,UAAU,OAAO;IAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;IACzD,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IACrC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IACrC,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC/E,OAAO,CACL,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;QACf,GAAG;QACH,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QAChB,GAAG;QACH,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC;QACjB,GAAG;QACH,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC;QACjB,GAAG;QACH,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CACd,CAAC,WAAW,EAAE,CAAC;AAClB,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { jwtDecode } from 'jwt-decode';
2
+ export function decodeToken(token) {
3
+ const data = jwtDecode(token);
4
+ return {
5
+ walletAddress: data.addr,
6
+ expiresAt: new Date(data.exp * 1000)
7
+ };
8
+ }
9
+ export function decodeRefreshToken(token) {
10
+ const data = jwtDecode(token);
11
+ return {
12
+ env: data.env,
13
+ expiresAt: new Date(data.exp * 1000)
14
+ };
15
+ }
16
+ export async function delay(ms) {
17
+ return new Promise((resolve) => setTimeout(resolve, ms));
18
+ }
19
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,MAAM,IAAI,GAAG,SAAS,CAA0D,KAAK,CAAC,CAAC;IACvF,OAAO;QACL,aAAa,EAAE,IAAI,CAAC,IAAI;QACxB,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;KACrC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,MAAM,IAAI,GAAG,SAAS,CAA0D,KAAK,CAAC,CAAC;IACvF,OAAO;QACL,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;KACrC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,EAAU;IACpC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ErrorData.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErrorData.js","sourceRoot":"","sources":["../../../src/types/ErrorData.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../../../src/types/json.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.js","sourceRoot":"","sources":["../../../src/types/token.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export const LIB_VERSION = '0.1.0';
2
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { LIB_VERSION } from './version';
2
+ //# sourceMappingURL=wallet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wallet.js","sourceRoot":"","sources":["../../src/wallet.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
package/package.json ADDED
@@ -0,0 +1,91 @@
1
+ {
2
+ "name": "@sogni-ai/expo-client",
3
+ "version": "1.0.0-alpha.2",
4
+ "description": "Sogni SDK for Expo/React Native - AI image, video & audio generation plus LLM chat via the Sogni Supernet",
5
+ "keywords": [
6
+ "sogni",
7
+ "expo",
8
+ "react-native",
9
+ "ai",
10
+ "image-generation",
11
+ "video-generation",
12
+ "stable-diffusion",
13
+ "flux",
14
+ "wan",
15
+ "ltx",
16
+ "text-to-image",
17
+ "text-to-video",
18
+ "generative-ai",
19
+ "llm",
20
+ "supernet"
21
+ ],
22
+ "main": "./dist/cjs/index.js",
23
+ "module": "./dist/esm/index.js",
24
+ "types": "./dist/cjs/index.d.ts",
25
+ "exports": {
26
+ ".": {
27
+ "import": "./dist/esm/index.js",
28
+ "require": "./dist/cjs/index.js",
29
+ "types": "./dist/cjs/index.d.ts"
30
+ },
31
+ "./wallet": {
32
+ "import": "./dist/esm/wallet.js",
33
+ "require": "./dist/cjs/wallet.js",
34
+ "types": "./dist/cjs/wallet.d.ts"
35
+ },
36
+ "./package.json": "./package.json"
37
+ },
38
+ "files": [
39
+ "dist/",
40
+ "src/",
41
+ "README.md",
42
+ "CHANGELOG.md"
43
+ ],
44
+ "scripts": {
45
+ "clean": "rimraf ./dist",
46
+ "build": "npm run clean && npm run build:cjs && npm run build:esm",
47
+ "build:cjs": "tsc --project tsconfig.json",
48
+ "build:esm": "tsc --project tsconfig.esm.json",
49
+ "watch": "tsc --watch --project tsconfig.json",
50
+ "prepublishOnly": "npm run build",
51
+ "prettier": "prettier --check ./src",
52
+ "prettier:fix": "prettier --write ./src",
53
+ "release": "semantic-release",
54
+ "prepare": "husky"
55
+ },
56
+ "repository": {
57
+ "type": "git",
58
+ "url": "git+https://github.com/Sogni-AI/expo-client.git"
59
+ },
60
+ "homepage": "https://github.com/Sogni-AI/expo-client#readme",
61
+ "bugs": {
62
+ "url": "https://github.com/Sogni-AI/expo-client/issues"
63
+ },
64
+ "author": "Sogni AI",
65
+ "license": "ISC",
66
+ "publishConfig": {
67
+ "access": "public"
68
+ },
69
+ "dependencies": {
70
+ "jwt-decode": "^4.0.0",
71
+ "viem": "^2.21.0"
72
+ },
73
+ "peerDependencies": {
74
+ "expo": ">=50.0.0",
75
+ "expo-secure-store": ">=12.0.0",
76
+ "expo-standard-web-crypto": ">=2.0.0",
77
+ "react-native": ">=0.73.0"
78
+ },
79
+ "devDependencies": {
80
+ "@commitlint/cli": "^19.0.0",
81
+ "@commitlint/config-conventional": "^19.0.0",
82
+ "@semantic-release/changelog": "^6.0.3",
83
+ "@semantic-release/git": "^10.0.1",
84
+ "expo-secure-store": "^55.0.13",
85
+ "husky": "^9.1.7",
86
+ "prettier": "^3.3.3",
87
+ "rimraf": "^6.0.1",
88
+ "semantic-release": "^24.2.1",
89
+ "typescript": "^5.5.3"
90
+ }
91
+ }
@@ -0,0 +1,118 @@
1
+ import ApiClient, { ApiError, ApiResponse } from '../ApiClient';
2
+ import TypedEventEmitter from '../lib/TypedEventEmitter';
3
+ import { AccountSnapshot, Balances, MeData, emptyAccountSnapshot } from './types';
4
+
5
+ interface AccountEvents {
6
+ change: AccountSnapshot;
7
+ }
8
+
9
+ /**
10
+ * Account API: current-user info, balance, logout.
11
+ *
12
+ * Phase 2 surface — `login()` and `signup()` are deferred to Phase 4
13
+ * because they require deterministic wallet derivation (Phase 4 wallet
14
+ * helper). For Phase 2, authenticate via API key in `SogniClient.create()`
15
+ * or pre-obtained tokens via `sogni.setTokens(...)`.
16
+ */
17
+ class AccountApi extends TypedEventEmitter<AccountEvents> {
18
+ private _client: ApiClient;
19
+ private _snapshot: AccountSnapshot;
20
+
21
+ constructor(client: ApiClient) {
22
+ super();
23
+ this._client = client;
24
+ this._snapshot = emptyAccountSnapshot();
25
+
26
+ client.socket.on('connected', ({ network }) => {
27
+ this._update({ networkStatus: 'connected', network });
28
+ });
29
+ client.socket.on('disconnected', () => {
30
+ this._update({ networkStatus: 'disconnected', network: null });
31
+ });
32
+ client.socket.on('authenticated', (e) => {
33
+ this._update({
34
+ authenticated: true,
35
+ username: e.username,
36
+ walletAddress: e.walletAddress,
37
+ balance: e.balance
38
+ });
39
+ });
40
+ client.socket.on('balanceUpdate', (e) => {
41
+ this._update({ balance: e.balance });
42
+ });
43
+ client.socket.on('changeNetwork', (e) => {
44
+ this._update({ network: e.network });
45
+ });
46
+ client.on('authUpdated', (authenticated) => {
47
+ if (!authenticated) {
48
+ this._update(emptyAccountSnapshot());
49
+ }
50
+ });
51
+ }
52
+
53
+ /** Read-only snapshot of the current account state. */
54
+ get current(): Readonly<AccountSnapshot> {
55
+ return this._snapshot;
56
+ }
57
+
58
+ /** Subscribe to account state changes. Returns an unsubscribe function. */
59
+ onChange(listener: (snapshot: AccountSnapshot) => void): () => void {
60
+ return this.on('change', listener);
61
+ }
62
+
63
+ /** Fetch the current user's profile data from REST. Updates the snapshot. */
64
+ async me(): Promise<MeData> {
65
+ const res = await this._client.rest.get<ApiResponse<MeData>>('/v1/account/me');
66
+ this._update({
67
+ username: res.data.username,
68
+ email: res.data.currentEmail,
69
+ walletAddress: res.data.walletAddress
70
+ });
71
+ return res.data;
72
+ }
73
+
74
+ /**
75
+ * Force-refresh the current user's balance from REST. Balance also updates
76
+ * automatically via socket events — call this only when you need a synchronous
77
+ * fetch (e.g., on screen mount before the socket has reconnected).
78
+ */
79
+ async refreshBalance(): Promise<Balances> {
80
+ const res = await this._client.rest.get<ApiResponse<Balances>>('/v4/account/balance');
81
+ this._update({ balance: res.data });
82
+ return res.data;
83
+ }
84
+
85
+ /**
86
+ * Log out and clear all auth state. Closes the WebSocket and removes any
87
+ * persisted tokens. Safe to call even if not authenticated.
88
+ */
89
+ async logout(): Promise<void> {
90
+ try {
91
+ await this._client.rest.post('/v1/account/logout');
92
+ } catch (e) {
93
+ if (e instanceof ApiError && e.status === 401) {
94
+ this._client.logger.warn('Logout call rejected — probably already logged out');
95
+ } else {
96
+ throw e;
97
+ }
98
+ }
99
+ this._client.auth.clear();
100
+ }
101
+
102
+ private _update(partial: Partial<AccountSnapshot>) {
103
+ const next: AccountSnapshot = { ...this._snapshot, ...partial };
104
+ if (shallowEqual(next, this._snapshot)) return;
105
+ this._snapshot = next;
106
+ this.emit('change', this._snapshot);
107
+ }
108
+ }
109
+
110
+ function shallowEqual<T extends object>(a: T, b: T): boolean {
111
+ const keys = Object.keys(a) as (keyof T)[];
112
+ for (const k of keys) {
113
+ if (a[k] !== b[k]) return false;
114
+ }
115
+ return true;
116
+ }
117
+
118
+ export default AccountApi;
@@ -0,0 +1,41 @@
1
+ import { Balances } from '../ApiClient/WebSocketClient/events';
2
+ import { SupernetType } from '../ApiClient/WebSocketClient/types';
3
+
4
+ export type { Balances };
5
+
6
+ export interface MeData {
7
+ currentEmail: string;
8
+ username: string;
9
+ walletAddress: string;
10
+ emailVerified?: boolean;
11
+ discord2FA?: boolean;
12
+ discordLinked?: boolean;
13
+ discordServerMember?: boolean;
14
+ discordUsername?: string;
15
+ requestedUpdatedEmail?: string;
16
+ }
17
+
18
+ export interface AccountSnapshot {
19
+ authenticated: boolean;
20
+ networkStatus: 'connected' | 'disconnected' | 'connecting';
21
+ network: SupernetType | null;
22
+ balance: Balances;
23
+ walletAddress: string | null;
24
+ username: string | null;
25
+ email: string | null;
26
+ }
27
+
28
+ export function emptyAccountSnapshot(): AccountSnapshot {
29
+ return {
30
+ authenticated: false,
31
+ networkStatus: 'disconnected',
32
+ network: null,
33
+ balance: {
34
+ sogni: { settled: '0', credit: '0', debit: '0', net: '0' },
35
+ spark: { settled: '0', credit: '0', debit: '0', net: '0', premiumCredit: '0' }
36
+ },
37
+ walletAddress: null,
38
+ username: null,
39
+ email: null
40
+ };
41
+ }
@@ -0,0 +1,60 @@
1
+ import { SupernetType } from './types';
2
+
3
+ export interface BalanceData {
4
+ settled: string;
5
+ credit: string;
6
+ debit: string;
7
+ net: string;
8
+ }
9
+
10
+ export interface SparkBalanceData extends BalanceData {
11
+ premiumCredit: string;
12
+ }
13
+
14
+ export interface Balances {
15
+ sogni: BalanceData;
16
+ spark: SparkBalanceData;
17
+ }
18
+
19
+ export interface AuthenticatedEvent {
20
+ username: string;
21
+ walletAddress: string;
22
+ balance: Balances;
23
+ isMainnet: boolean;
24
+ accountWasMigrated: boolean;
25
+ hasUnclaimedAirdrop: boolean;
26
+ firstLoginAfterMigration: boolean;
27
+ }
28
+
29
+ export interface BalanceUpdateEvent {
30
+ balance: Balances;
31
+ }
32
+
33
+ export interface ChangeNetworkEvent {
34
+ network: SupernetType;
35
+ }
36
+
37
+ export interface ConnectedEvent {
38
+ network: SupernetType;
39
+ }
40
+
41
+ export interface DisconnectedEvent {
42
+ code?: number;
43
+ reason?: string;
44
+ }
45
+
46
+ export interface ToastMessageEvent {
47
+ level: 'info' | 'success' | 'warning' | 'error';
48
+ message: string;
49
+ stickyID?: string;
50
+ }
51
+
52
+ /** Public event map for WebSocketClient. */
53
+ export interface SocketEventMap {
54
+ connected: ConnectedEvent;
55
+ disconnected: DisconnectedEvent;
56
+ authenticated: AuthenticatedEvent;
57
+ balanceUpdate: BalanceUpdateEvent;
58
+ changeNetwork: ChangeNetworkEvent;
59
+ toastMessage: ToastMessageEvent;
60
+ }
@@ -0,0 +1,224 @@
1
+ import TypedEventEmitter from '../../lib/TypedEventEmitter';
2
+ import { base64Decode, base64Encode } from '../../lib/base64';
3
+ import { Logger } from '../../lib/DefaultLogger';
4
+ import { AuthManager } from '../../lib/AuthManager';
5
+ import { LIB_VERSION } from '../../version';
6
+ import { ConnectionStatus, SupernetType } from './types';
7
+ import { SocketEventMap } from './events';
8
+ import {
9
+ normalizeAuthenticated,
10
+ normalizeBalanceUpdate,
11
+ normalizeChangeNetwork,
12
+ normalizeToastMessage
13
+ } from './normalize';
14
+ import { RawAuthenticated, RawBalanceUpdate, RawChangeNetwork, RawToastMessage } from './wireTypes';
15
+
16
+ const PROTOCOL_VERSION = '3.0.0';
17
+ const RECONNECT_DELAY_MS = 1000;
18
+ const MAX_RECONNECT_ATTEMPTS = 5;
19
+
20
+ /** RN WebSocket constructor accepts a third options argument with `headers`. */
21
+ type WebSocketCtorOptions = { headers?: Record<string, string> } | undefined;
22
+ type WebSocketCtor = new (
23
+ url: string,
24
+ protocols?: string | string[] | null,
25
+ options?: WebSocketCtorOptions
26
+ ) => WebSocket;
27
+
28
+ class WebSocketClient extends TypedEventEmitter<SocketEventMap> {
29
+ readonly appId: string;
30
+ readonly appSource?: string;
31
+ readonly baseUrl: string;
32
+ protected _logger: Logger;
33
+ private _auth: AuthManager;
34
+ private _supernetType: SupernetType;
35
+ private socket: WebSocket | null = null;
36
+ private _status: ConnectionStatus = 'disconnected';
37
+ private _reconnectAttempts = 0;
38
+ private _wantConnected = false;
39
+
40
+ constructor(
41
+ baseUrl: string,
42
+ auth: AuthManager,
43
+ appId: string,
44
+ supernetType: SupernetType,
45
+ logger: Logger,
46
+ appSource?: string
47
+ ) {
48
+ super();
49
+ this.baseUrl = normalizeBaseUrl(baseUrl);
50
+ this._auth = auth;
51
+ this.appId = appId;
52
+ this.appSource = appSource?.trim() || undefined;
53
+ this._supernetType = supernetType;
54
+ this._logger = logger;
55
+ }
56
+
57
+ get supernetType(): SupernetType {
58
+ return this._supernetType;
59
+ }
60
+
61
+ get isConnected(): boolean {
62
+ return this._status === 'connected';
63
+ }
64
+
65
+ get status(): ConnectionStatus {
66
+ return this._status;
67
+ }
68
+
69
+ async connect(): Promise<void> {
70
+ this._wantConnected = true;
71
+ if (this.socket) {
72
+ this._cleanupSocket();
73
+ }
74
+ this._status = 'connecting';
75
+
76
+ const userAgent = `Sogni/${PROTOCOL_VERSION} (sogni-expo-client) ${LIB_VERSION}`;
77
+ const url = new URL(this.baseUrl);
78
+ url.protocol = url.protocol === 'https:' || url.protocol === 'wss:' ? 'wss:' : 'ws:';
79
+ url.searchParams.set('appId', this.appId);
80
+ if (this.appSource) url.searchParams.set('appSource', this.appSource);
81
+ url.searchParams.set('clientName', userAgent);
82
+ url.searchParams.set('clientType', 'artist');
83
+ url.searchParams.set('forceWorkerId', this._supernetType === 'fast' ? 'fast' : '');
84
+
85
+ const auth = await this._auth.socketAuth();
86
+ if (auth.queryParams) {
87
+ for (const [k, v] of Object.entries(auth.queryParams)) {
88
+ url.searchParams.set(k, v);
89
+ }
90
+ }
91
+
92
+ const options: WebSocketCtorOptions = auth.headers ? { headers: auth.headers } : undefined;
93
+ const Ctor = (globalThis as unknown as { WebSocket: WebSocketCtor }).WebSocket;
94
+ if (!Ctor) {
95
+ throw new Error('No global WebSocket available — are you running outside of React Native?');
96
+ }
97
+ const socket = new Ctor(url.toString(), undefined, options);
98
+ this.socket = socket;
99
+ socket.onopen = () => this._handleOpen();
100
+ socket.onmessage = (e) => this._handleMessage(e);
101
+ socket.onclose = (e) => this._handleClose(e);
102
+ socket.onerror = (e) => this._handleError(e);
103
+ }
104
+
105
+ disconnect(): void {
106
+ this._wantConnected = false;
107
+ if (!this.socket) return;
108
+ const socket = this.socket;
109
+ this._cleanupSocket();
110
+ try {
111
+ socket.close(1000, 'Client disconnected');
112
+ } catch (e) {
113
+ this._logger.warn('Error closing socket:', e);
114
+ }
115
+ this._status = 'disconnected';
116
+ this.emit('disconnected', { code: 1000, reason: 'Client disconnected' });
117
+ }
118
+
119
+ async switchNetwork(target: SupernetType): Promise<SupernetType> {
120
+ return new Promise<SupernetType>((resolve) => {
121
+ this.once('changeNetwork', ({ network }) => {
122
+ this._supernetType = network;
123
+ resolve(network);
124
+ });
125
+ void this.send('changeNetwork', target);
126
+ });
127
+ }
128
+
129
+ async send(messageType: string, data: unknown): Promise<void> {
130
+ if (!this.socket || this._status !== 'connected') {
131
+ throw new Error(`Cannot send ${messageType}: socket not connected`);
132
+ }
133
+ const envelope = JSON.stringify({
134
+ type: messageType,
135
+ data: base64Encode(JSON.stringify(data))
136
+ });
137
+ this._logger.debug('WebSocket send:', messageType, data);
138
+ this.socket.send(envelope);
139
+ }
140
+
141
+ private _handleOpen() {
142
+ this._status = 'connected';
143
+ this._reconnectAttempts = 0;
144
+ this.emit('connected', { network: this._supernetType });
145
+ }
146
+
147
+ private _handleClose(e: { code?: number; reason?: string }) {
148
+ const wasConnected = this._status === 'connected';
149
+ this._status = 'disconnected';
150
+ this._cleanupSocket();
151
+ this.emit('disconnected', { code: e.code, reason: e.reason });
152
+ if (this._wantConnected && wasConnected && e.code !== 1000) {
153
+ this._scheduleReconnect();
154
+ }
155
+ }
156
+
157
+ private _handleError(e: unknown) {
158
+ this._logger.error('WebSocket error:', e);
159
+ }
160
+
161
+ private _scheduleReconnect() {
162
+ if (this._reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
163
+ this._logger.warn('WebSocket reconnect attempts exhausted');
164
+ return;
165
+ }
166
+ this._reconnectAttempts++;
167
+ setTimeout(() => {
168
+ if (this._wantConnected) {
169
+ void this.connect();
170
+ }
171
+ }, RECONNECT_DELAY_MS);
172
+ }
173
+
174
+ private _cleanupSocket() {
175
+ if (!this.socket) return;
176
+ const s = this.socket;
177
+ s.onopen = null;
178
+ s.onmessage = null;
179
+ s.onclose = null;
180
+ s.onerror = null;
181
+ this.socket = null;
182
+ }
183
+
184
+ private _handleMessage(e: MessageEvent) {
185
+ Promise.resolve(typeof e.data === 'string' ? e.data : (e.data as Blob).text())
186
+ .then((str: string) => {
187
+ const envelope = JSON.parse(str) as { type: string; data?: string };
188
+ const payload: any = envelope.data ? JSON.parse(base64Decode(envelope.data)) : null;
189
+ this._logger.debug('WebSocket recv:', envelope.type, payload);
190
+ this._dispatch(envelope.type, payload);
191
+ })
192
+ .catch((err) => this._logger.error('Failed to parse WebSocket message:', err));
193
+ }
194
+
195
+ private _dispatch(type: string, payload: any) {
196
+ switch (type) {
197
+ case 'authenticated':
198
+ this.emit('authenticated', normalizeAuthenticated(payload as RawAuthenticated));
199
+ break;
200
+ case 'balanceUpdate':
201
+ this.emit('balanceUpdate', normalizeBalanceUpdate(payload as RawBalanceUpdate));
202
+ break;
203
+ case 'changeNetwork':
204
+ this.emit('changeNetwork', normalizeChangeNetwork(payload as RawChangeNetwork));
205
+ break;
206
+ case 'toastMessage':
207
+ this.emit('toastMessage', normalizeToastMessage(payload as RawToastMessage));
208
+ break;
209
+ default:
210
+ // Phase 2 only handles a subset; later phases will add jobState, jobProgress, etc.
211
+ this._logger.debug('Unhandled WebSocket message type:', type);
212
+ break;
213
+ }
214
+ }
215
+ }
216
+
217
+ function normalizeBaseUrl(url: string): string {
218
+ const u = new URL(url);
219
+ if (u.protocol === 'http:' || u.protocol === 'ws:') u.protocol = 'http:';
220
+ else u.protocol = 'https:';
221
+ return u.toString();
222
+ }
223
+
224
+ export default WebSocketClient;