@sogni-ai/sogni-client 0.3.1 → 0.3.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.
Files changed (38) hide show
  1. package/README.md +1 -1
  2. package/dist/version.d.ts +1 -1
  3. package/dist/version.js +1 -1
  4. package/package.json +5 -3
  5. package/src/Account/CurrentAccount.ts +101 -0
  6. package/src/Account/index.ts +243 -0
  7. package/src/Account/types.ts +90 -0
  8. package/src/ApiClient/WebSocketClient/ErrorCode.ts +15 -0
  9. package/src/ApiClient/WebSocketClient/events.ts +94 -0
  10. package/src/ApiClient/WebSocketClient/index.ts +203 -0
  11. package/src/ApiClient/WebSocketClient/messages.ts +7 -0
  12. package/src/ApiClient/WebSocketClient/types.ts +1 -0
  13. package/src/ApiClient/events.ts +20 -0
  14. package/src/ApiClient/index.ts +124 -0
  15. package/src/ApiGroup.ts +25 -0
  16. package/src/Projects/Job.ts +124 -0
  17. package/src/Projects/Project.ts +185 -0
  18. package/src/Projects/createJobRequestMessage.ts +99 -0
  19. package/src/Projects/index.ts +340 -0
  20. package/src/Projects/models.json +8906 -0
  21. package/src/Projects/types/EstimationResponse.ts +45 -0
  22. package/src/Projects/types/events.ts +78 -0
  23. package/src/Projects/types/index.ts +146 -0
  24. package/src/Stats/index.ts +15 -0
  25. package/src/Stats/types.ts +34 -0
  26. package/src/events.ts +5 -0
  27. package/src/index.ts +120 -0
  28. package/src/lib/DataEntity.ts +38 -0
  29. package/src/lib/DefaultLogger.ts +47 -0
  30. package/src/lib/EIP712Helper.ts +57 -0
  31. package/src/lib/RestClient.ts +76 -0
  32. package/src/lib/TypedEventEmitter.ts +66 -0
  33. package/src/lib/base64.ts +9 -0
  34. package/src/lib/getUUID.ts +8 -0
  35. package/src/lib/isNodejs.ts +4 -0
  36. package/src/types/ErrorData.ts +6 -0
  37. package/src/types/json.ts +5 -0
  38. package/src/version.ts +1 -0
package/README.md CHANGED
@@ -91,7 +91,7 @@ const project = await client.projects.create({
91
91
  numberOfImages: 1
92
92
  });
93
93
  ```
94
- **Note:** Full project parameter list can be found in [ProjectParams](https://sogni-ai.github.io/sogni-client/interfaces/ProjectParams.html) docs.
94
+ **Note:** Full project parameter list can be found in [ProjectParams](https://sdk-docs.sogni.ai/interfaces/ProjectParams.html) docs.
95
95
 
96
96
  ### Getting project status and results
97
97
  In general there are 2 ways to work with API:
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const LIB_VERSION = "0.3.1";
1
+ export declare const LIB_VERSION = "0.3.3";
package/dist/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LIB_VERSION = void 0;
4
- exports.LIB_VERSION = "0.3.1";
4
+ exports.LIB_VERSION = "0.3.3";
5
5
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -3,13 +3,14 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.3.1",
6
+ "version": "0.3.3",
7
7
  "description": "Sogni Supernet Client",
8
8
  "main": "dist/index.js",
9
9
  "types": "dist/index.d.ts",
10
10
  "files": [
11
11
  "README.md",
12
- "dist/"
12
+ "dist/",
13
+ "src/"
13
14
  ],
14
15
  "scripts": {
15
16
  "clean": "rimraf ./dist",
@@ -20,7 +21,8 @@
20
21
  "watch:esm": "npm run clean && tsc --watch --project tsconfig.esm.json",
21
22
  "prettier": "prettier --check ./src",
22
23
  "prettier:fix": "prettier --write ./src",
23
- "docs:build": "npx typedoc"
24
+ "docs": "npx typedoc",
25
+ "postdocs": "node -p \"'sdk-docs.sogni.ai'\" > docs/CNAME"
24
26
  },
25
27
  "repository": {
26
28
  "type": "git",
@@ -0,0 +1,101 @@
1
+ import DataEntity from '../lib/DataEntity';
2
+ import { BalanceData } from './types';
3
+ import { jwtDecode } from 'jwt-decode';
4
+ import { SupernetType } from '../ApiClient/WebSocketClient/types';
5
+ /**
6
+ * @inline
7
+ */
8
+ export interface AccountData {
9
+ token: string | null;
10
+ networkStatus: 'connected' | 'disconnected' | 'connecting';
11
+ network: SupernetType | null;
12
+ balance: BalanceData;
13
+ walletAddress?: string;
14
+ expiresAt?: Date;
15
+ username?: string;
16
+ }
17
+
18
+ function getDefaults(): AccountData {
19
+ return {
20
+ token: null,
21
+ networkStatus: 'disconnected',
22
+ network: null,
23
+ balance: {
24
+ credit: '0',
25
+ debit: '0',
26
+ net: '0',
27
+ settled: '0'
28
+ }
29
+ };
30
+ }
31
+
32
+ function decodeToken(token: string) {
33
+ const data = jwtDecode<{ addr: string; env: string; iat: number; exp: number }>(token);
34
+ return {
35
+ walletAddress: data.addr,
36
+ expiresAt: new Date(data.exp * 1000)
37
+ };
38
+ }
39
+
40
+ /**
41
+ * Current account data.
42
+ * @expand
43
+ */
44
+ class CurrentAccount extends DataEntity<AccountData> {
45
+ constructor(data?: AccountData) {
46
+ super(data || getDefaults());
47
+ }
48
+
49
+ _update<K extends keyof AccountData>(delta: Partial<AccountData>) {
50
+ this.data = { ...this.data, ...(delta as Partial<AccountData>) };
51
+ const keys = Object.keys(delta);
52
+ if (delta.hasOwnProperty('token')) {
53
+ if (delta.token) {
54
+ Object.assign(this.data, decodeToken(delta.token));
55
+ } else {
56
+ delete this.data.walletAddress;
57
+ delete this.data.expiresAt;
58
+ }
59
+ keys.push('walletAddress', 'expiresAt');
60
+ }
61
+ this.emit('updated', keys);
62
+ }
63
+
64
+ _clear() {
65
+ this._update(getDefaults());
66
+ }
67
+
68
+ get isAuthenicated() {
69
+ return !!this.data.token && !!this.data.expiresAt && this.data.expiresAt > new Date();
70
+ }
71
+
72
+ get networkStatus() {
73
+ return this.data.networkStatus;
74
+ }
75
+
76
+ get network() {
77
+ return this.data.network;
78
+ }
79
+
80
+ get balance() {
81
+ return this.data.balance;
82
+ }
83
+
84
+ get walletAddress() {
85
+ return this.data.walletAddress;
86
+ }
87
+
88
+ get expiresAt() {
89
+ return this.data.expiresAt;
90
+ }
91
+
92
+ get username() {
93
+ return this.data.username;
94
+ }
95
+
96
+ get token() {
97
+ return this.data.token;
98
+ }
99
+ }
100
+
101
+ export default CurrentAccount;
@@ -0,0 +1,243 @@
1
+ import {
2
+ AccountCreateData,
3
+ BalanceData,
4
+ LoginData,
5
+ Nonce,
6
+ Reward,
7
+ RewardRaw,
8
+ TxHistoryData,
9
+ TxHistoryEntry,
10
+ TxHistoryParams
11
+ } from './types';
12
+ import ApiGroup, { ApiConfig } from '../ApiGroup';
13
+ import { Wallet, pbkdf2, toUtf8Bytes } from 'ethers';
14
+ import { ApiError, ApiReponse } from '../ApiClient';
15
+ import CurrentAccount from './CurrentAccount';
16
+ import { SupernetType } from '../ApiClient/WebSocketClient/types';
17
+
18
+ /**
19
+ * Account API methods that let you interact with the user's account.
20
+ */
21
+ class AccountApi extends ApiGroup {
22
+ readonly currentAccount = new CurrentAccount();
23
+
24
+ constructor(config: ApiConfig) {
25
+ super(config);
26
+ this.client.socket.on('balanceUpdate', this.handleBalanceUpdate.bind(this));
27
+ this.client.on('connected', this.handleServerConnected.bind(this));
28
+ this.client.on('disconnected', this.handleServerDisconnected.bind(this));
29
+ }
30
+
31
+ private handleBalanceUpdate(data: BalanceData) {
32
+ this.currentAccount._update({ balance: data });
33
+ }
34
+
35
+ private handleServerConnected({ network }: { network: SupernetType }) {
36
+ this.currentAccount._update({
37
+ networkStatus: 'connected',
38
+ network
39
+ });
40
+ }
41
+
42
+ private handleServerDisconnected() {
43
+ this.currentAccount._clear();
44
+ }
45
+
46
+ async getNonce(walletAddress: string): Promise<string> {
47
+ const res = await this.client.rest.post<ApiReponse<Nonce>>('/v1/account/nonce', {
48
+ walletAddress
49
+ });
50
+ return res.data.nonce;
51
+ }
52
+
53
+ getWallet(username: string, password: string): Wallet {
54
+ const pwd = toUtf8Bytes(username.toLowerCase() + password);
55
+ const salt = toUtf8Bytes('sogni-salt-value');
56
+ const pkey = pbkdf2(pwd, salt, 10000, 32, 'sha256');
57
+ return new Wallet(pkey, this.provider);
58
+ }
59
+
60
+ async create(
61
+ username: string,
62
+ email: string,
63
+ password: string,
64
+ subscribe = false,
65
+ referralCode?: string
66
+ ): Promise<AccountCreateData> {
67
+ const wallet = this.getWallet(username, password);
68
+ const nonce = await this.getNonce(wallet.address);
69
+ const payload = {
70
+ appid: this.client.appId,
71
+ username,
72
+ email,
73
+ subscribe: subscribe ? 1 : 0,
74
+ walletAddress: wallet.address
75
+ };
76
+ const signature = await this.eip712.signTypedData(wallet, 'signup', { ...payload, nonce });
77
+ const res = await this.client.rest.post<ApiReponse<AccountCreateData>>('/v1/account/create', {
78
+ ...payload,
79
+ referralCode,
80
+ signature
81
+ });
82
+ this.setToken(username, res.data.token);
83
+ return res.data;
84
+ }
85
+
86
+ setToken(username: string, token: string): void {
87
+ this.client.authenticate(token);
88
+ this.currentAccount._update({
89
+ token,
90
+ username
91
+ });
92
+ }
93
+
94
+ /**
95
+ * Login with username and password. WebSocket connection is established after successful login.
96
+ * @param username
97
+ * @param password
98
+ */
99
+ async login(username: string, password: string): Promise<LoginData> {
100
+ const wallet = this.getWallet(username, password);
101
+ const nonce = await this.getNonce(wallet.address);
102
+ const signature = await this.eip712.signTypedData(wallet, 'authentication', {
103
+ walletAddress: wallet.address,
104
+ nonce
105
+ });
106
+ const res = await this.client.rest.post<ApiReponse<LoginData>>('/v1/account/login', {
107
+ walletAddress: wallet.address,
108
+ signature
109
+ });
110
+ this.setToken(username, res.data.token);
111
+ return res.data;
112
+ }
113
+
114
+ /**
115
+ * Logout the user and close the WebSocket connection.
116
+ */
117
+ async logout(): Promise<void> {
118
+ this.client.rest.post('/v1/account/logout').catch((e) => {
119
+ this.client.logger.error('Failed to logout', e);
120
+ });
121
+ this.client.removeAuth();
122
+ this.currentAccount._clear();
123
+ }
124
+
125
+ /**
126
+ * Refresh the balance of the current account.
127
+ */
128
+ async refreshBalance(): Promise<BalanceData> {
129
+ const res = await this.client.rest.get<ApiReponse<BalanceData>>('/v1/account/balance');
130
+ this.currentAccount._update({ balance: res.data });
131
+ return res.data;
132
+ }
133
+
134
+ /**
135
+ * Get the balance of a account wallet.
136
+ * @param walletAddress
137
+ */
138
+ async walletBalance(walletAddress: string) {
139
+ const res = await this.client.rest.get<ApiReponse<{ token: string; ether: string }>>(
140
+ '/v1/wallet/balance',
141
+ {
142
+ walletAddress
143
+ }
144
+ );
145
+ return res.data;
146
+ }
147
+
148
+ async validateUsername(username: string) {
149
+ try {
150
+ return await this.client.rest.post<ApiReponse<undefined>>('/v1/account/username/validate', {
151
+ username
152
+ });
153
+ } catch (e) {
154
+ if (e instanceof ApiError) {
155
+ // Username is already taken
156
+ if (e.payload.errorCode === 108) {
157
+ return e.payload;
158
+ }
159
+ }
160
+ throw e;
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Switch between fast and relaxed networks.
166
+ * Note: This method will close the current WebSocket connection and establish a new one.
167
+ * Do not call this method if you have any active projects.
168
+ * @param network
169
+ */
170
+ async switchNetwork(network: SupernetType) {
171
+ this.currentAccount._update({
172
+ networkStatus: 'connecting',
173
+ network: null
174
+ });
175
+ this.client.socket.switchNetwork(network);
176
+ }
177
+
178
+ /**
179
+ * Get the transaction history of the current account.
180
+ * @param params
181
+ */
182
+ async transactionHistory(
183
+ params: TxHistoryParams
184
+ ): Promise<{ entries: TxHistoryEntry[]; next: TxHistoryParams }> {
185
+ const res = await this.client.rest.get<ApiReponse<TxHistoryData>>('/v1/transactions/list', {
186
+ status: params.status,
187
+ address: params.address,
188
+ limit: params.limit.toString()
189
+ });
190
+
191
+ return {
192
+ entries: res.data.transactions.map(
193
+ (tx): TxHistoryEntry => ({
194
+ id: tx.id,
195
+ address: tx.address,
196
+ createTime: new Date(tx.createTime),
197
+ updateTime: new Date(tx.updateTime),
198
+ status: tx.status,
199
+ role: tx.role,
200
+ amount: tx.amount,
201
+ description: tx.description,
202
+ source: tx.source,
203
+ endTime: new Date(tx.endTime),
204
+ type: tx.type
205
+ })
206
+ ),
207
+ next: {
208
+ ...params,
209
+ offset: res.data.next
210
+ }
211
+ };
212
+ }
213
+
214
+ async rewards(): Promise<Reward[]> {
215
+ const r =
216
+ await this.client.rest.get<ApiReponse<{ rewards: RewardRaw[] }>>('/v2/account/rewards');
217
+
218
+ return r.data.rewards.map(
219
+ (raw: RewardRaw): Reward => ({
220
+ id: raw.id,
221
+ type: raw.type,
222
+ title: raw.title,
223
+ description: raw.description,
224
+ amount: raw.amount,
225
+ claimed: !!raw.claimed,
226
+ canClaim: !!raw.canClaim,
227
+ lastClaim: new Date(raw.lastClaimTimestamp * 1000),
228
+ nextClaim:
229
+ raw.lastClaimTimestamp && raw.claimResetFrequencySec > -1
230
+ ? new Date(raw.lastClaimTimestamp * 1000 + raw.claimResetFrequencySec * 1000)
231
+ : null
232
+ })
233
+ );
234
+ }
235
+
236
+ async claimRewards(rewardIds: string[]): Promise<void> {
237
+ await this.client.rest.post('/v2/account/reward/claim', {
238
+ claims: rewardIds
239
+ });
240
+ }
241
+ }
242
+
243
+ export default AccountApi;
@@ -0,0 +1,90 @@
1
+ export interface Nonce {
2
+ nonce: string;
3
+ }
4
+
5
+ export interface AccountCreateData {
6
+ token: string;
7
+ }
8
+
9
+ export interface LoginData {
10
+ token: string;
11
+ username: string;
12
+ }
13
+
14
+ export interface BalanceData {
15
+ settled: string;
16
+ credit: string;
17
+ debit: string;
18
+ net: string;
19
+ }
20
+
21
+ export interface TxHistoryParams {
22
+ status: 'completed';
23
+ address: string;
24
+ limit: number;
25
+ offset?: number;
26
+ }
27
+
28
+ export interface TxHistoryData {
29
+ transactions: TxRaw[];
30
+ next: number;
31
+ }
32
+
33
+ export interface TxRaw {
34
+ _id: string;
35
+ id: string;
36
+ SID: number;
37
+ address: string;
38
+ createTime: number;
39
+ updateTime: number;
40
+ status: 'completed';
41
+ role: 'artist' | 'worker';
42
+ clientSID: number;
43
+ addressSID: number;
44
+ amount: number;
45
+ description: string;
46
+ source: 'project' | string;
47
+ sourceSID: string;
48
+ endTime: number;
49
+ type: 'debit' | string;
50
+ }
51
+
52
+ export interface TxHistoryEntry {
53
+ id: string;
54
+ address: string;
55
+ createTime: Date;
56
+ updateTime: Date;
57
+ status: 'completed';
58
+ role: 'artist' | 'worker';
59
+ amount: number;
60
+ description: string;
61
+ source: 'project' | string;
62
+ endTime: Date;
63
+ type: 'debit' | string;
64
+ }
65
+
66
+ export type RewardType = 'instant' | 'conditioned';
67
+
68
+ export interface RewardRaw {
69
+ id: string;
70
+ type: RewardType;
71
+ title: string;
72
+ description: string;
73
+ amount: string;
74
+ claimed: number;
75
+ canClaim: number;
76
+ lastClaimTimestamp: number;
77
+ claimResetFrequencySec: number;
78
+ }
79
+
80
+ export interface Reward {
81
+ id: string;
82
+ type: RewardType;
83
+ title: string;
84
+ description: string;
85
+ amount: string;
86
+ claimed: boolean;
87
+ canClaim: boolean;
88
+ lastClaim: Date;
89
+ nextClaim: Date | null;
90
+ }
@@ -0,0 +1,15 @@
1
+ export enum ErrorCode {
2
+ // App ID is blocked from connecting
3
+ APP_ID_BLOCKED = 4010,
4
+ // New connection from same app-id, server will switch to it
5
+ SWITCH_CONNECTION = 4015,
6
+ // Authentication error happened
7
+ AUTH_ERROR = 4021
8
+ }
9
+
10
+ export function isNotRecoverable(code: ErrorCode) {
11
+ //check if code is in 4xxx
12
+ return code >= 4000 && code < 5000;
13
+ }
14
+
15
+ export default ErrorCode;
@@ -0,0 +1,94 @@
1
+ import { SupernetType } from './types';
2
+
3
+ export type BalanceData = {
4
+ settled: string;
5
+ credit: string;
6
+ debit: string;
7
+ net: string;
8
+ };
9
+
10
+ export type JobErrorData = {
11
+ jobID: string;
12
+ imgID?: string;
13
+ isFromWorker: boolean;
14
+ error_message: string;
15
+ error: number;
16
+ };
17
+
18
+ export type JobProgressData = {
19
+ jobID: string;
20
+ imgID: string;
21
+ hasImage: boolean;
22
+ step: number;
23
+ stepCount: number;
24
+ };
25
+
26
+ export type JobResultData = {
27
+ jobID: string;
28
+ imgID: string;
29
+ performedStepCount: number;
30
+ lastSeed: string;
31
+ userCanceled: boolean;
32
+ triggeredNSFWFilter: boolean;
33
+ };
34
+
35
+ export type JobStateData =
36
+ | {
37
+ type: 'initiatingModel' | 'jobStarted';
38
+ jobID: string;
39
+ imgID: string;
40
+ workerName: string;
41
+ }
42
+ | {
43
+ jobID: string;
44
+ type: 'queued';
45
+ queuePosition: number;
46
+ }
47
+ | {
48
+ type: 'jobCompleted';
49
+ jobID: string;
50
+ };
51
+
52
+ export type ServerConnectData = {
53
+ network: SupernetType;
54
+ };
55
+
56
+ export type ServerDisconnectData = {
57
+ code: number;
58
+ reason: string;
59
+ };
60
+
61
+ export type SocketEventMap = {
62
+ /**
63
+ * @event WebSocketClient#balanceUpdate - Received balance update
64
+ */
65
+ balanceUpdate: BalanceData;
66
+ /**
67
+ * @event WebSocketClient#jobError - Job error occurred
68
+ */
69
+ jobError: JobErrorData;
70
+ /**
71
+ * @event WebSocketClient#jobProgress - Job progress update
72
+ */
73
+ jobProgress: JobProgressData;
74
+ /**
75
+ * @event WebSocketClient#jobResult - Job result received
76
+ */
77
+ jobResult: JobResultData;
78
+ /**
79
+ * @event WebSocketClient#jobState - Job state changed
80
+ */
81
+ jobState: JobStateData;
82
+ /**
83
+ * @event WebSocketClient#swarmModels - Received swarm model count
84
+ */
85
+ swarmModels: Record<string, number>;
86
+ /**
87
+ * @event WebSocketClient#connected - WebSocket connection opened
88
+ */
89
+ connected: ServerConnectData;
90
+ /**
91
+ * @event WebSocketClient#disconnected - WebSocket connection was closed
92
+ */
93
+ disconnected: ServerDisconnectData;
94
+ };