chatablex-web-sdk 1.0.3 → 1.0.32

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/dist/index.js CHANGED
@@ -22,6 +22,10 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  Bridge: () => Bridge,
24
24
  ChatableX: () => ChatableX,
25
+ CloudAuthRequiredError: () => CloudAuthRequiredError,
26
+ CloudError: () => CloudError,
27
+ CloudQuotaExceededError: () => CloudQuotaExceededError,
28
+ CloudSubscriptionRequiredError: () => CloudSubscriptionRequiredError,
25
29
  SDK_VERSION: () => SDK_VERSION
26
30
  });
27
31
  module.exports = __toCommonJS(index_exports);
@@ -299,10 +303,246 @@ function createPlatformModule(bridge) {
299
303
  };
300
304
  }
301
305
 
306
+ // src/modules/auth.ts
307
+ var EXPIRY_SKEW_MS = 5e3;
308
+ function isValid(token, now) {
309
+ return !!token && typeof token.access_token === "string" && token.access_token.length > 0 && token.expires_at - EXPIRY_SKEW_MS > now;
310
+ }
311
+ var HostAuthProvider = class {
312
+ constructor(bridge) {
313
+ this._token = null;
314
+ /** In-flight refresh, so concurrent callers share one host round-trip. */
315
+ this._refreshing = null;
316
+ this._bridge = bridge;
317
+ }
318
+ async getToken() {
319
+ if (isValid(this._token, Date.now())) return this._token;
320
+ const ok = await this.refresh();
321
+ return ok ? this._token : null;
322
+ }
323
+ async getAuthHeaders() {
324
+ const token = await this.getToken();
325
+ if (!token) return {};
326
+ return { Authorization: `Bearer ${token.access_token}` };
327
+ }
328
+ getUserId() {
329
+ return this._token?.user_id ?? null;
330
+ }
331
+ isAuthenticated() {
332
+ return isValid(this._token, Date.now());
333
+ }
334
+ refresh() {
335
+ if (this._refreshing) return this._refreshing;
336
+ this._refreshing = this._doRefresh().finally(() => {
337
+ this._refreshing = null;
338
+ });
339
+ return this._refreshing;
340
+ }
341
+ async _doRefresh() {
342
+ try {
343
+ const raw = await this._bridge.sendMessage("host.getAuthToken");
344
+ if (raw && typeof raw === "object" && typeof raw.access_token === "string" && raw.access_token.length > 0) {
345
+ this._token = {
346
+ access_token: raw.access_token,
347
+ expires_at: Number(raw.expires_at) || 0,
348
+ user_id: String(raw.user_id ?? "")
349
+ };
350
+ return true;
351
+ }
352
+ this._token = null;
353
+ return false;
354
+ } catch {
355
+ this._token = null;
356
+ return false;
357
+ }
358
+ }
359
+ };
360
+ function createAuthModule(bridge) {
361
+ return new HostAuthProvider(bridge);
362
+ }
363
+
364
+ // src/modules/cloud.ts
365
+ var CloudError = class extends Error {
366
+ constructor(message, code) {
367
+ super(message);
368
+ this.name = "CloudError";
369
+ this.code = code;
370
+ }
371
+ };
372
+ var CloudAuthRequiredError = class extends CloudError {
373
+ constructor(message = "cloud storage requires an authenticated session") {
374
+ super(message, 401);
375
+ this.name = "CloudAuthRequiredError";
376
+ }
377
+ };
378
+ var CloudSubscriptionRequiredError = class extends CloudError {
379
+ constructor(message = "cloud storage requires an active subscription") {
380
+ super(message, 40302);
381
+ this.name = "CloudSubscriptionRequiredError";
382
+ }
383
+ };
384
+ var CloudQuotaExceededError = class extends CloudError {
385
+ constructor(usedBytes, quotaBytes, message = "storage quota exceeded") {
386
+ super(message, 40301);
387
+ this.name = "CloudQuotaExceededError";
388
+ this.usedBytes = usedBytes;
389
+ this.quotaBytes = quotaBytes;
390
+ }
391
+ };
392
+ var DEFAULT_CONTENT_TYPE = "application/octet-stream";
393
+ function normalizeData(data) {
394
+ if (typeof Blob !== "undefined" && data instanceof Blob) {
395
+ return { body: data, size: data.size, type: data.type || "" };
396
+ }
397
+ if (typeof data === "string") {
398
+ const blob = new Blob([data]);
399
+ return { body: blob, size: blob.size, type: "" };
400
+ }
401
+ if (data instanceof ArrayBuffer) {
402
+ return { body: data, size: data.byteLength, type: "" };
403
+ }
404
+ if (ArrayBuffer.isView(data)) {
405
+ return { body: data, size: data.byteLength, type: "" };
406
+ }
407
+ throw new CloudError("unsupported upload data type", 400);
408
+ }
409
+ function createCloudModule(bridge, deps) {
410
+ const { appId, auth } = deps;
411
+ let resolvedBase = deps.apiBaseUrl ? stripTrailingSlash(deps.apiBaseUrl) : null;
412
+ function stripTrailingSlash(url) {
413
+ return url.replace(/\/+$/, "");
414
+ }
415
+ async function baseUrl() {
416
+ if (resolvedBase) return resolvedBase;
417
+ try {
418
+ const r = await bridge.sendMessage("host.getApiBaseUrl", {}, 5e3);
419
+ const url = typeof r === "string" ? r : r && typeof r === "object" && typeof r.base_url === "string" ? r.base_url : "";
420
+ if (url) {
421
+ resolvedBase = stripTrailingSlash(url);
422
+ return resolvedBase;
423
+ }
424
+ } catch {
425
+ }
426
+ throw new CloudError(
427
+ "cloud API base URL is not configured; pass apiBaseUrl to ChatableX.init()",
428
+ 0
429
+ );
430
+ }
431
+ async function authedFetch(path, init) {
432
+ const token = await auth.getToken();
433
+ if (!token) throw new CloudAuthRequiredError();
434
+ const base = await baseUrl();
435
+ const url = `${base}${path}`;
436
+ const build = async () => ({
437
+ ...init,
438
+ headers: { ...init.headers ?? {}, ...await auth.getAuthHeaders() }
439
+ });
440
+ let res = await fetch(url, await build());
441
+ if (res.status === 401 && await auth.refresh()) {
442
+ res = await fetch(url, await build());
443
+ }
444
+ return res;
445
+ }
446
+ async function callApi(path, init) {
447
+ const res = await authedFetch(path, init);
448
+ let body = null;
449
+ try {
450
+ body = await res.json();
451
+ } catch {
452
+ }
453
+ const code = body?.code;
454
+ const message = body?.message || `HTTP ${res.status}`;
455
+ if (!res.ok || body?.success === false) {
456
+ if (code === 40301) {
457
+ const q = body?.data ?? {};
458
+ throw new CloudQuotaExceededError(q.used_bytes ?? 0, q.quota_bytes ?? 0, message);
459
+ }
460
+ if (code === 40302) throw new CloudSubscriptionRequiredError(message);
461
+ if (res.status === 401) throw new CloudAuthRequiredError(message);
462
+ throw new CloudError(message, code ?? res.status);
463
+ }
464
+ return body?.data ?? null;
465
+ }
466
+ function validateFileKey(fileKey) {
467
+ if (!fileKey || typeof fileKey !== "string") {
468
+ throw new CloudError("fileKey is required", 400);
469
+ }
470
+ }
471
+ return {
472
+ async upload(fileKey, data, options = {}) {
473
+ validateFileKey(fileKey);
474
+ const { body, size, type } = normalizeData(data);
475
+ const contentType = options.contentType || type || DEFAULT_CONTENT_TYPE;
476
+ const signed = await callApi("/api/storage/upload-url", {
477
+ method: "POST",
478
+ headers: { "Content-Type": "application/json" },
479
+ body: JSON.stringify({
480
+ app_id: appId,
481
+ file_key: fileKey,
482
+ content_type: contentType,
483
+ size_bytes: size
484
+ })
485
+ });
486
+ const put = await fetch(signed.upload_url, {
487
+ method: "PUT",
488
+ headers: { "Content-Type": contentType },
489
+ body
490
+ });
491
+ if (!put.ok) {
492
+ throw new CloudError(`OSS upload failed: HTTP ${put.status}`, put.status);
493
+ }
494
+ return { fileKey, objectKey: signed.object_key, size, contentType };
495
+ },
496
+ async getDownloadUrl(fileKey) {
497
+ validateFileKey(fileKey);
498
+ const signed = await callApi("/api/storage/download-url", {
499
+ method: "POST",
500
+ headers: { "Content-Type": "application/json" },
501
+ body: JSON.stringify({ app_id: appId, file_key: fileKey })
502
+ });
503
+ return signed.download_url;
504
+ },
505
+ async download(fileKey) {
506
+ const url = await this.getDownloadUrl(fileKey);
507
+ const res = await fetch(url, { method: "GET" });
508
+ if (!res.ok) {
509
+ throw new CloudError(`OSS download failed: HTTP ${res.status}`, res.status);
510
+ }
511
+ return res.blob();
512
+ },
513
+ async list(options = {}) {
514
+ const qs = new URLSearchParams({ app_id: appId });
515
+ if (options.prefix) qs.set("prefix", options.prefix);
516
+ const data = await callApi(`/api/storage/files?${qs.toString()}`, {
517
+ method: "GET"
518
+ });
519
+ return (data?.files ?? []).map((f) => ({
520
+ fileKey: f.file_key,
521
+ size: f.size,
522
+ lastModified: f.last_modified
523
+ }));
524
+ },
525
+ async delete(fileKey) {
526
+ validateFileKey(fileKey);
527
+ const qs = new URLSearchParams({ app_id: appId, file_key: fileKey });
528
+ await callApi(`/api/storage/files?${qs.toString()}`, { method: "DELETE" });
529
+ },
530
+ async usage() {
531
+ const data = await callApi("/api/storage/usage", { method: "GET" });
532
+ return {
533
+ usedBytes: data.used_bytes,
534
+ quotaBytes: data.quota_bytes,
535
+ fileCount: data.file_count,
536
+ reconciledAt: data.reconciled_at
537
+ };
538
+ }
539
+ };
540
+ }
541
+
302
542
  // package.json
303
543
  var package_default = {
304
544
  name: "chatablex-web-sdk",
305
- version: "1.0.3",
545
+ version: "1.0.32",
306
546
  description: "ChatableX Web SDK for AI App WebUI development. Provides bridge communication with the ChatableX Flutter client.",
307
547
  main: "dist/index.js",
308
548
  module: "dist/index.mjs",
@@ -394,6 +634,7 @@ var ChatableX = {
394
634
  }
395
635
  const toolModule = createToolModule(bridge, config.appId);
396
636
  if (toolConfig) toolModule._setInfo(toolConfig);
637
+ const authModule = createAuthModule(bridge);
397
638
  const sdk = {
398
639
  ai: createAIModule(bridge),
399
640
  tools: createToolsModule(bridge),
@@ -401,7 +642,13 @@ var ChatableX = {
401
642
  events: createEventsModule(bridge),
402
643
  storage: createStorageModule(bridge),
403
644
  tool: toolModule,
404
- platform: createPlatformModule(bridge)
645
+ platform: createPlatformModule(bridge),
646
+ auth: authModule,
647
+ cloud: createCloudModule(bridge, {
648
+ appId: config.appId,
649
+ auth: authModule,
650
+ apiBaseUrl: config.apiBaseUrl
651
+ })
405
652
  };
406
653
  window.ChatableX = sdk;
407
654
  _instance = sdk;
@@ -424,5 +671,9 @@ var ChatableX = {
424
671
  0 && (module.exports = {
425
672
  Bridge,
426
673
  ChatableX,
674
+ CloudAuthRequiredError,
675
+ CloudError,
676
+ CloudQuotaExceededError,
677
+ CloudSubscriptionRequiredError,
427
678
  SDK_VERSION
428
679
  });
package/dist/index.mjs CHANGED
@@ -271,10 +271,246 @@ function createPlatformModule(bridge) {
271
271
  };
272
272
  }
273
273
 
274
+ // src/modules/auth.ts
275
+ var EXPIRY_SKEW_MS = 5e3;
276
+ function isValid(token, now) {
277
+ return !!token && typeof token.access_token === "string" && token.access_token.length > 0 && token.expires_at - EXPIRY_SKEW_MS > now;
278
+ }
279
+ var HostAuthProvider = class {
280
+ constructor(bridge) {
281
+ this._token = null;
282
+ /** In-flight refresh, so concurrent callers share one host round-trip. */
283
+ this._refreshing = null;
284
+ this._bridge = bridge;
285
+ }
286
+ async getToken() {
287
+ if (isValid(this._token, Date.now())) return this._token;
288
+ const ok = await this.refresh();
289
+ return ok ? this._token : null;
290
+ }
291
+ async getAuthHeaders() {
292
+ const token = await this.getToken();
293
+ if (!token) return {};
294
+ return { Authorization: `Bearer ${token.access_token}` };
295
+ }
296
+ getUserId() {
297
+ return this._token?.user_id ?? null;
298
+ }
299
+ isAuthenticated() {
300
+ return isValid(this._token, Date.now());
301
+ }
302
+ refresh() {
303
+ if (this._refreshing) return this._refreshing;
304
+ this._refreshing = this._doRefresh().finally(() => {
305
+ this._refreshing = null;
306
+ });
307
+ return this._refreshing;
308
+ }
309
+ async _doRefresh() {
310
+ try {
311
+ const raw = await this._bridge.sendMessage("host.getAuthToken");
312
+ if (raw && typeof raw === "object" && typeof raw.access_token === "string" && raw.access_token.length > 0) {
313
+ this._token = {
314
+ access_token: raw.access_token,
315
+ expires_at: Number(raw.expires_at) || 0,
316
+ user_id: String(raw.user_id ?? "")
317
+ };
318
+ return true;
319
+ }
320
+ this._token = null;
321
+ return false;
322
+ } catch {
323
+ this._token = null;
324
+ return false;
325
+ }
326
+ }
327
+ };
328
+ function createAuthModule(bridge) {
329
+ return new HostAuthProvider(bridge);
330
+ }
331
+
332
+ // src/modules/cloud.ts
333
+ var CloudError = class extends Error {
334
+ constructor(message, code) {
335
+ super(message);
336
+ this.name = "CloudError";
337
+ this.code = code;
338
+ }
339
+ };
340
+ var CloudAuthRequiredError = class extends CloudError {
341
+ constructor(message = "cloud storage requires an authenticated session") {
342
+ super(message, 401);
343
+ this.name = "CloudAuthRequiredError";
344
+ }
345
+ };
346
+ var CloudSubscriptionRequiredError = class extends CloudError {
347
+ constructor(message = "cloud storage requires an active subscription") {
348
+ super(message, 40302);
349
+ this.name = "CloudSubscriptionRequiredError";
350
+ }
351
+ };
352
+ var CloudQuotaExceededError = class extends CloudError {
353
+ constructor(usedBytes, quotaBytes, message = "storage quota exceeded") {
354
+ super(message, 40301);
355
+ this.name = "CloudQuotaExceededError";
356
+ this.usedBytes = usedBytes;
357
+ this.quotaBytes = quotaBytes;
358
+ }
359
+ };
360
+ var DEFAULT_CONTENT_TYPE = "application/octet-stream";
361
+ function normalizeData(data) {
362
+ if (typeof Blob !== "undefined" && data instanceof Blob) {
363
+ return { body: data, size: data.size, type: data.type || "" };
364
+ }
365
+ if (typeof data === "string") {
366
+ const blob = new Blob([data]);
367
+ return { body: blob, size: blob.size, type: "" };
368
+ }
369
+ if (data instanceof ArrayBuffer) {
370
+ return { body: data, size: data.byteLength, type: "" };
371
+ }
372
+ if (ArrayBuffer.isView(data)) {
373
+ return { body: data, size: data.byteLength, type: "" };
374
+ }
375
+ throw new CloudError("unsupported upload data type", 400);
376
+ }
377
+ function createCloudModule(bridge, deps) {
378
+ const { appId, auth } = deps;
379
+ let resolvedBase = deps.apiBaseUrl ? stripTrailingSlash(deps.apiBaseUrl) : null;
380
+ function stripTrailingSlash(url) {
381
+ return url.replace(/\/+$/, "");
382
+ }
383
+ async function baseUrl() {
384
+ if (resolvedBase) return resolvedBase;
385
+ try {
386
+ const r = await bridge.sendMessage("host.getApiBaseUrl", {}, 5e3);
387
+ const url = typeof r === "string" ? r : r && typeof r === "object" && typeof r.base_url === "string" ? r.base_url : "";
388
+ if (url) {
389
+ resolvedBase = stripTrailingSlash(url);
390
+ return resolvedBase;
391
+ }
392
+ } catch {
393
+ }
394
+ throw new CloudError(
395
+ "cloud API base URL is not configured; pass apiBaseUrl to ChatableX.init()",
396
+ 0
397
+ );
398
+ }
399
+ async function authedFetch(path, init) {
400
+ const token = await auth.getToken();
401
+ if (!token) throw new CloudAuthRequiredError();
402
+ const base = await baseUrl();
403
+ const url = `${base}${path}`;
404
+ const build = async () => ({
405
+ ...init,
406
+ headers: { ...init.headers ?? {}, ...await auth.getAuthHeaders() }
407
+ });
408
+ let res = await fetch(url, await build());
409
+ if (res.status === 401 && await auth.refresh()) {
410
+ res = await fetch(url, await build());
411
+ }
412
+ return res;
413
+ }
414
+ async function callApi(path, init) {
415
+ const res = await authedFetch(path, init);
416
+ let body = null;
417
+ try {
418
+ body = await res.json();
419
+ } catch {
420
+ }
421
+ const code = body?.code;
422
+ const message = body?.message || `HTTP ${res.status}`;
423
+ if (!res.ok || body?.success === false) {
424
+ if (code === 40301) {
425
+ const q = body?.data ?? {};
426
+ throw new CloudQuotaExceededError(q.used_bytes ?? 0, q.quota_bytes ?? 0, message);
427
+ }
428
+ if (code === 40302) throw new CloudSubscriptionRequiredError(message);
429
+ if (res.status === 401) throw new CloudAuthRequiredError(message);
430
+ throw new CloudError(message, code ?? res.status);
431
+ }
432
+ return body?.data ?? null;
433
+ }
434
+ function validateFileKey(fileKey) {
435
+ if (!fileKey || typeof fileKey !== "string") {
436
+ throw new CloudError("fileKey is required", 400);
437
+ }
438
+ }
439
+ return {
440
+ async upload(fileKey, data, options = {}) {
441
+ validateFileKey(fileKey);
442
+ const { body, size, type } = normalizeData(data);
443
+ const contentType = options.contentType || type || DEFAULT_CONTENT_TYPE;
444
+ const signed = await callApi("/api/storage/upload-url", {
445
+ method: "POST",
446
+ headers: { "Content-Type": "application/json" },
447
+ body: JSON.stringify({
448
+ app_id: appId,
449
+ file_key: fileKey,
450
+ content_type: contentType,
451
+ size_bytes: size
452
+ })
453
+ });
454
+ const put = await fetch(signed.upload_url, {
455
+ method: "PUT",
456
+ headers: { "Content-Type": contentType },
457
+ body
458
+ });
459
+ if (!put.ok) {
460
+ throw new CloudError(`OSS upload failed: HTTP ${put.status}`, put.status);
461
+ }
462
+ return { fileKey, objectKey: signed.object_key, size, contentType };
463
+ },
464
+ async getDownloadUrl(fileKey) {
465
+ validateFileKey(fileKey);
466
+ const signed = await callApi("/api/storage/download-url", {
467
+ method: "POST",
468
+ headers: { "Content-Type": "application/json" },
469
+ body: JSON.stringify({ app_id: appId, file_key: fileKey })
470
+ });
471
+ return signed.download_url;
472
+ },
473
+ async download(fileKey) {
474
+ const url = await this.getDownloadUrl(fileKey);
475
+ const res = await fetch(url, { method: "GET" });
476
+ if (!res.ok) {
477
+ throw new CloudError(`OSS download failed: HTTP ${res.status}`, res.status);
478
+ }
479
+ return res.blob();
480
+ },
481
+ async list(options = {}) {
482
+ const qs = new URLSearchParams({ app_id: appId });
483
+ if (options.prefix) qs.set("prefix", options.prefix);
484
+ const data = await callApi(`/api/storage/files?${qs.toString()}`, {
485
+ method: "GET"
486
+ });
487
+ return (data?.files ?? []).map((f) => ({
488
+ fileKey: f.file_key,
489
+ size: f.size,
490
+ lastModified: f.last_modified
491
+ }));
492
+ },
493
+ async delete(fileKey) {
494
+ validateFileKey(fileKey);
495
+ const qs = new URLSearchParams({ app_id: appId, file_key: fileKey });
496
+ await callApi(`/api/storage/files?${qs.toString()}`, { method: "DELETE" });
497
+ },
498
+ async usage() {
499
+ const data = await callApi("/api/storage/usage", { method: "GET" });
500
+ return {
501
+ usedBytes: data.used_bytes,
502
+ quotaBytes: data.quota_bytes,
503
+ fileCount: data.file_count,
504
+ reconciledAt: data.reconciled_at
505
+ };
506
+ }
507
+ };
508
+ }
509
+
274
510
  // package.json
275
511
  var package_default = {
276
512
  name: "chatablex-web-sdk",
277
- version: "1.0.3",
513
+ version: "1.0.32",
278
514
  description: "ChatableX Web SDK for AI App WebUI development. Provides bridge communication with the ChatableX Flutter client.",
279
515
  main: "dist/index.js",
280
516
  module: "dist/index.mjs",
@@ -366,6 +602,7 @@ var ChatableX = {
366
602
  }
367
603
  const toolModule = createToolModule(bridge, config.appId);
368
604
  if (toolConfig) toolModule._setInfo(toolConfig);
605
+ const authModule = createAuthModule(bridge);
369
606
  const sdk = {
370
607
  ai: createAIModule(bridge),
371
608
  tools: createToolsModule(bridge),
@@ -373,7 +610,13 @@ var ChatableX = {
373
610
  events: createEventsModule(bridge),
374
611
  storage: createStorageModule(bridge),
375
612
  tool: toolModule,
376
- platform: createPlatformModule(bridge)
613
+ platform: createPlatformModule(bridge),
614
+ auth: authModule,
615
+ cloud: createCloudModule(bridge, {
616
+ appId: config.appId,
617
+ auth: authModule,
618
+ apiBaseUrl: config.apiBaseUrl
619
+ })
377
620
  };
378
621
  window.ChatableX = sdk;
379
622
  _instance = sdk;
@@ -395,5 +638,9 @@ var ChatableX = {
395
638
  export {
396
639
  Bridge,
397
640
  ChatableX,
641
+ CloudAuthRequiredError,
642
+ CloudError,
643
+ CloudQuotaExceededError,
644
+ CloudSubscriptionRequiredError,
398
645
  SDK_VERSION
399
646
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chatablex-web-sdk",
3
- "version": "1.0.3",
3
+ "version": "1.0.32",
4
4
  "description": "ChatableX Web SDK for AI App WebUI development. Provides bridge communication with the ChatableX Flutter client.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
package/src/index.ts CHANGED
@@ -26,6 +26,8 @@ import { createUIModule } from './modules/ui';
26
26
  import { createStorageModule } from './modules/storage';
27
27
  import { createToolsModule } from './modules/tools';
28
28
  import { createPlatformModule } from './modules/platform';
29
+ import { createAuthModule } from './modules/auth';
30
+ import { createCloudModule } from './modules/cloud';
29
31
  import type { ChatableXSDK, ChatableXInitConfig, ToolInfo } from './types';
30
32
  import pkg from '../package.json';
31
33
 
@@ -78,6 +80,8 @@ export const ChatableX = {
78
80
  const toolModule = createToolModule(bridge, config.appId);
79
81
  if (toolConfig) toolModule._setInfo(toolConfig);
80
82
 
83
+ const authModule = createAuthModule(bridge);
84
+
81
85
  const sdk: ChatableXSDK = {
82
86
  ai: createAIModule(bridge),
83
87
  tools: createToolsModule(bridge),
@@ -86,6 +90,12 @@ export const ChatableX = {
86
90
  storage: createStorageModule(bridge),
87
91
  tool: toolModule,
88
92
  platform: createPlatformModule(bridge),
93
+ auth: authModule,
94
+ cloud: createCloudModule(bridge, {
95
+ appId: config.appId,
96
+ auth: authModule,
97
+ apiBaseUrl: config.apiBaseUrl,
98
+ }),
89
99
  };
90
100
 
91
101
  // Expose on window for debugging / Flutter interop
@@ -114,3 +124,9 @@ export const ChatableX = {
114
124
  // Re-export all types
115
125
  export * from './types';
116
126
  export { Bridge } from './bridge';
127
+ export {
128
+ CloudError,
129
+ CloudAuthRequiredError,
130
+ CloudSubscriptionRequiredError,
131
+ CloudQuotaExceededError,
132
+ } from './modules/cloud';