@layr-labs/ecloud-sdk 0.2.2-dev → 0.3.0-dev

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.
package/VERSION CHANGED
@@ -1,2 +1,2 @@
1
- version=0.2.2-dev
2
- commit=549da0a4121d064a66af11f15a509934858d9e2d
1
+ version=0.3.0-dev
2
+ commit=f66f41c4db37c3eb732f093499934884d8132a71
package/dist/billing.cjs CHANGED
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
8
11
  var __export = (target, all) => {
9
12
  for (var name in all)
10
13
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -27,6 +30,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
30
  ));
28
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
32
 
33
+ // src/client/common/auth/session.ts
34
+ var init_session = __esm({
35
+ "src/client/common/auth/session.ts"() {
36
+ "use strict";
37
+ }
38
+ });
39
+
30
40
  // src/billing.ts
31
41
  var billing_exports = {};
32
42
  __export(billing_exports, {
@@ -74,22 +84,213 @@ async function calculateBillingAuthSignature(options) {
74
84
  return { signature, expiry };
75
85
  }
76
86
 
87
+ // src/client/common/auth/billingSession.ts
88
+ var BillingSessionError = class extends Error {
89
+ constructor(message, code, statusCode) {
90
+ super(message);
91
+ this.code = code;
92
+ this.statusCode = statusCode;
93
+ this.name = "BillingSessionError";
94
+ }
95
+ };
96
+ function stripHexPrefix(hex) {
97
+ return hex.startsWith("0x") ? hex.slice(2) : hex;
98
+ }
99
+ async function parseErrorResponse(response) {
100
+ try {
101
+ const data = await response.json();
102
+ return data.error || response.statusText;
103
+ } catch {
104
+ return response.statusText;
105
+ }
106
+ }
107
+ async function loginToBillingApi(config, request) {
108
+ let response;
109
+ try {
110
+ response = await fetch(`${config.baseUrl}/auth/siwe/login`, {
111
+ method: "POST",
112
+ credentials: "include",
113
+ // Include cookies for session management
114
+ headers: {
115
+ "Content-Type": "application/json"
116
+ },
117
+ body: JSON.stringify({
118
+ message: request.message,
119
+ signature: stripHexPrefix(request.signature)
120
+ })
121
+ });
122
+ } catch (error) {
123
+ throw new BillingSessionError(
124
+ `Network error connecting to ${config.baseUrl}: ${error instanceof Error ? error.message : String(error)}`,
125
+ "NETWORK_ERROR"
126
+ );
127
+ }
128
+ if (!response.ok) {
129
+ const errorMessage = await parseErrorResponse(response);
130
+ const status = response.status;
131
+ if (status === 400) {
132
+ if (errorMessage.toLowerCase().includes("siwe")) {
133
+ throw new BillingSessionError(`Invalid SIWE message: ${errorMessage}`, "INVALID_MESSAGE", status);
134
+ }
135
+ throw new BillingSessionError(`Bad request: ${errorMessage}`, "INVALID_MESSAGE", status);
136
+ }
137
+ if (status === 401) {
138
+ throw new BillingSessionError(`Invalid signature: ${errorMessage}`, "INVALID_SIGNATURE", status);
139
+ }
140
+ throw new BillingSessionError(`Login failed: ${errorMessage}`, "UNKNOWN", status);
141
+ }
142
+ const data = await response.json();
143
+ return {
144
+ success: data.success,
145
+ address: data.address
146
+ };
147
+ }
148
+ async function getBillingApiSession(config) {
149
+ let response;
150
+ try {
151
+ response = await fetch(`${config.baseUrl}/auth/session`, {
152
+ method: "GET",
153
+ credentials: "include",
154
+ // Include cookies for session management
155
+ headers: {
156
+ "Content-Type": "application/json"
157
+ }
158
+ });
159
+ } catch {
160
+ return {
161
+ authenticated: false
162
+ };
163
+ }
164
+ if (response.status === 401) {
165
+ return {
166
+ authenticated: false
167
+ };
168
+ }
169
+ if (!response.ok) {
170
+ const errorMessage = await parseErrorResponse(response);
171
+ throw new BillingSessionError(`Failed to get session: ${errorMessage}`, "UNKNOWN", response.status);
172
+ }
173
+ const data = await response.json();
174
+ return {
175
+ authenticated: data.authenticated,
176
+ address: data.address,
177
+ chainId: data.chainId,
178
+ authenticatedAt: data.authenticatedAt
179
+ };
180
+ }
181
+ async function logoutFromBillingApi(config) {
182
+ let response;
183
+ try {
184
+ response = await fetch(`${config.baseUrl}/auth/logout`, {
185
+ method: "POST",
186
+ credentials: "include",
187
+ // Include cookies for session management
188
+ headers: {
189
+ "Content-Type": "application/json"
190
+ }
191
+ });
192
+ } catch (error) {
193
+ throw new BillingSessionError(
194
+ `Network error connecting to ${config.baseUrl}: ${error instanceof Error ? error.message : String(error)}`,
195
+ "NETWORK_ERROR"
196
+ );
197
+ }
198
+ if (response.status === 401) {
199
+ return;
200
+ }
201
+ if (!response.ok) {
202
+ const errorMessage = await parseErrorResponse(response);
203
+ throw new BillingSessionError(`Logout failed: ${errorMessage}`, "UNKNOWN", response.status);
204
+ }
205
+ }
206
+
77
207
  // src/client/common/utils/billingapi.ts
78
208
  var BillingApiClient = class {
79
- constructor(config, walletClient) {
209
+ constructor(config, walletClient, options = {}) {
80
210
  this.config = config;
81
211
  this.walletClient = walletClient;
212
+ this.options = options;
213
+ this.useSession = options.useSession ?? false;
214
+ if (!this.useSession && !walletClient) {
215
+ throw new Error("WalletClient is required when not using session authentication");
216
+ }
82
217
  }
83
218
  /**
84
219
  * Get the address of the connected wallet
220
+ * Returns undefined if using session auth without a wallet client
85
221
  */
86
222
  get address() {
87
- const account = this.walletClient.account;
223
+ const account = this.walletClient?.account;
88
224
  if (!account) {
89
- throw new Error("WalletClient must have an account attached");
225
+ if (!this.useSession) {
226
+ throw new Error("WalletClient must have an account attached");
227
+ }
228
+ return void 0;
90
229
  }
91
230
  return account.address;
92
231
  }
232
+ /**
233
+ * Get the base URL of the billing API
234
+ */
235
+ get baseUrl() {
236
+ return this.config.billingApiServerURL;
237
+ }
238
+ // ==========================================================================
239
+ // SIWE Session Methods
240
+ // ==========================================================================
241
+ /**
242
+ * Login to the billing API using SIWE
243
+ *
244
+ * This establishes a session with the billing API by verifying the SIWE message
245
+ * and signature. On success, a session cookie is set in the browser.
246
+ *
247
+ * @param request - Login request containing SIWE message and signature
248
+ * @returns Login result with the authenticated address
249
+ *
250
+ * @example
251
+ * ```typescript
252
+ * const { message } = createSiweMessage({
253
+ * address: userAddress,
254
+ * chainId: 11155111,
255
+ * domain: window.location.host,
256
+ * uri: window.location.origin,
257
+ * });
258
+ *
259
+ * const signature = await signMessageAsync({ message });
260
+ * const result = await billingClient.siweLogin({ message, signature });
261
+ * ```
262
+ */
263
+ async siweLogin(request) {
264
+ return loginToBillingApi({ baseUrl: this.baseUrl }, request);
265
+ }
266
+ /**
267
+ * Logout from the billing API
268
+ *
269
+ * This destroys the current session and clears the session cookie.
270
+ */
271
+ async siweLogout() {
272
+ return logoutFromBillingApi({ baseUrl: this.baseUrl });
273
+ }
274
+ /**
275
+ * Get the current session status from the billing API
276
+ *
277
+ * @returns Session information including authentication status and address
278
+ */
279
+ async getSession() {
280
+ return getBillingApiSession({ baseUrl: this.baseUrl });
281
+ }
282
+ /**
283
+ * Check if there is a valid session
284
+ *
285
+ * @returns True if session is authenticated, false otherwise
286
+ */
287
+ async isSessionValid() {
288
+ const session = await this.getSession();
289
+ return session.authenticated;
290
+ }
291
+ // ==========================================================================
292
+ // Subscription Methods
293
+ // ==========================================================================
93
294
  async createSubscription(productId = "compute", options) {
94
295
  const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
95
296
  const body = options ? {
@@ -108,10 +309,72 @@ var BillingApiClient = class {
108
309
  const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
109
310
  await this.makeAuthenticatedRequest(endpoint, "DELETE", productId);
110
311
  }
312
+ // ==========================================================================
313
+ // Internal Methods
314
+ // ==========================================================================
111
315
  /**
112
316
  * Make an authenticated request to the billing API
317
+ *
318
+ * Uses session auth if useSession is true, otherwise uses EIP-712 signature auth.
113
319
  */
114
320
  async makeAuthenticatedRequest(url, method, productId, body) {
321
+ if (this.useSession) {
322
+ return this.makeSessionAuthenticatedRequest(url, method, body);
323
+ }
324
+ return this.makeSignatureAuthenticatedRequest(url, method, productId, body);
325
+ }
326
+ /**
327
+ * Make a request using session-based authentication (cookies)
328
+ */
329
+ async makeSessionAuthenticatedRequest(url, method, body) {
330
+ const headers = {};
331
+ if (body) {
332
+ headers["Content-Type"] = "application/json";
333
+ }
334
+ try {
335
+ const response = await fetch(url, {
336
+ method,
337
+ credentials: "include",
338
+ // Include cookies for session management
339
+ headers,
340
+ body: body ? JSON.stringify(body) : void 0
341
+ });
342
+ const status = response.status;
343
+ const statusText = status >= 200 && status < 300 ? "OK" : "Error";
344
+ if (status < 200 || status >= 300) {
345
+ let errorBody;
346
+ try {
347
+ errorBody = await response.text();
348
+ } catch {
349
+ errorBody = statusText;
350
+ }
351
+ throw new Error(`BillingAPI request failed: ${status} ${statusText} - ${errorBody}`);
352
+ }
353
+ const responseData = await response.json();
354
+ return {
355
+ json: async () => responseData,
356
+ text: async () => JSON.stringify(responseData)
357
+ };
358
+ } catch (error) {
359
+ if (error.name === "TypeError" || error.message?.includes("fetch")) {
360
+ throw new Error(
361
+ `Failed to connect to BillingAPI at ${url}: ${error.message}
362
+ Please check:
363
+ 1. Your internet connection
364
+ 2. The API server is accessible: ${this.config.billingApiServerURL}
365
+ 3. Firewall/proxy settings`
366
+ );
367
+ }
368
+ throw error;
369
+ }
370
+ }
371
+ /**
372
+ * Make a request using EIP-712 signature authentication
373
+ */
374
+ async makeSignatureAuthenticatedRequest(url, method, productId, body) {
375
+ if (!this.walletClient) {
376
+ throw new Error("WalletClient is required for signature authentication");
377
+ }
115
378
  const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
116
379
  const { signature } = await calculateBillingAuthSignature({
117
380
  walletClient: this.walletClient,
@@ -257,6 +520,9 @@ var import_accounts = require("viem/accounts");
257
520
  // src/client/common/constants.ts
258
521
  var import_chains = require("viem/chains");
259
522
 
523
+ // src/client/common/utils/userapi.ts
524
+ init_session();
525
+
260
526
  // src/client/common/utils/billing.ts
261
527
  function isSubscriptionActive(status) {
262
528
  return status === "active" || status === "trialing";
@@ -497,7 +763,10 @@ function createBillingModule(config) {
497
763
  };
498
764
  }
499
765
  logger.debug(`Creating subscription for ${productId}...`);
500
- const result = await billingApi.createSubscription(productId);
766
+ const result = await billingApi.createSubscription(productId, {
767
+ successUrl: opts?.successUrl,
768
+ cancelUrl: opts?.cancelUrl
769
+ });
501
770
  logger.debug(`Checkout URL: ${result.checkoutUrl}`);
502
771
  return {
503
772
  type: "checkout_created",