dexie-cloud-addon 4.3.2 → 4.3.4

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.
@@ -1,5 +1,5 @@
1
1
  import { DexieCloudOptions } from './DexieCloudOptions';
2
- import { DBRealmRole, DexieCloudSchema } from 'dexie-cloud-common';
2
+ import { DBRealmRole, DexieCloudSchema, AuthProvidersResponse } from 'dexie-cloud-common';
3
3
  import { UserLogin } from './db/entities/UserLogin';
4
4
  import { PersistedSyncState } from './db/entities/PersistedSyncState';
5
5
  import { SyncState } from './types/SyncState';
@@ -76,4 +76,15 @@ export interface DexieCloudAPI {
76
76
  * @param table Table name that the object was retrieved from
77
77
  */
78
78
  permissions<T>(obj: T, table: string): Observable<PermissionChecker<T, string>>;
79
+ /** Query available authentication providers from the server.
80
+ *
81
+ * Returns information about which OAuth providers are configured
82
+ * and whether OTP (email) authentication is enabled.
83
+ *
84
+ * Useful for apps that want to build their own login UI and show
85
+ * provider-specific buttons.
86
+ *
87
+ * @returns Promise resolving to available auth providers
88
+ */
89
+ getAuthProviders(): Promise<AuthProvidersResponse>;
79
90
  }
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * ==========================================================================
10
10
  *
11
- * Version 4.3.2, Thu Jan 22 2026
11
+ * Version 4.3.4, Fri Jan 23 2026
12
12
  *
13
13
  * https://dexie.org
14
14
  *
@@ -1025,6 +1025,21 @@ function promptForProvider(userInteraction_1, providers_1, otpEnabled_1) {
1025
1025
  });
1026
1026
  }
1027
1027
 
1028
+ /**
1029
+ * Error thrown when initiating an OAuth redirect.
1030
+ *
1031
+ * This is not a real error - it's used to signal that the page is
1032
+ * navigating away to an OAuth provider. It should be caught and
1033
+ * silently ignored at the appropriate level.
1034
+ */
1035
+ class OAuthRedirectError extends Error {
1036
+ constructor(provider) {
1037
+ super(`OAuth redirect initiated for provider: ${provider}`);
1038
+ this.name = 'OAuthRedirectError';
1039
+ this.provider = provider;
1040
+ }
1041
+ }
1042
+
1028
1043
  function loadAccessToken(db) {
1029
1044
  return __awaiter(this, void 0, void 0, function* () {
1030
1045
  var _a, _b, _c;
@@ -1192,6 +1207,10 @@ function userAuthenticate(context, fetchToken, userInteraction, hints) {
1192
1207
  return context;
1193
1208
  }
1194
1209
  catch (error) {
1210
+ // OAuth redirect is not an error - page is navigating away
1211
+ if (error instanceof OAuthRedirectError || (error === null || error === void 0 ? void 0 : error.name) === 'OAuthRedirectError') {
1212
+ throw error; // Re-throw without logging
1213
+ }
1195
1214
  if (error instanceof TokenErrorResponseError) {
1196
1215
  yield alertUser(userInteraction, error.title, {
1197
1216
  type: 'error',
@@ -1554,7 +1573,7 @@ function otpFetchTokenCallback(db) {
1554
1573
  if (hints === null || hints === void 0 ? void 0 : hints.provider) {
1555
1574
  initiateOAuthRedirect(db, hints.provider);
1556
1575
  // This function never returns - page navigates away
1557
- throw new Error('OAuth redirect initiated');
1576
+ throw new OAuthRedirectError(hints.provider);
1558
1577
  }
1559
1578
  if ((hints === null || hints === void 0 ? void 0 : hints.grant_type) === 'demo') {
1560
1579
  const demo_user = yield promptForEmail(userInteraction, 'Enter a demo user email', (hints === null || hints === void 0 ? void 0 : hints.email) || (hints === null || hints === void 0 ? void 0 : hints.userId));
@@ -1589,7 +1608,7 @@ function otpFetchTokenCallback(db) {
1589
1608
  // User selected an OAuth provider - initiate redirect
1590
1609
  initiateOAuthRedirect(db, selection.provider);
1591
1610
  // This function never returns - page navigates away
1592
- throw new Error('OAuth redirect initiated');
1611
+ throw new OAuthRedirectError(selection.provider);
1593
1612
  }
1594
1613
  // User chose OTP - continue with email prompt below
1595
1614
  }
@@ -1778,7 +1797,15 @@ function login(db, hints) {
1778
1797
  claims: {},
1779
1798
  lastLogin: new Date(0),
1780
1799
  });
1781
- yield authenticate(db.cloud.options.databaseUrl, context, db.cloud.options.fetchTokens || otpFetchTokenCallback(db), db.cloud.userInteraction, hints);
1800
+ try {
1801
+ yield authenticate(db.cloud.options.databaseUrl, context, db.cloud.options.fetchTokens || otpFetchTokenCallback(db), db.cloud.userInteraction, hints);
1802
+ }
1803
+ catch (err) {
1804
+ if (err.name === 'OAuthRedirectError') {
1805
+ return false; // Page is redirecting for OAuth login
1806
+ }
1807
+ throw err;
1808
+ }
1782
1809
  if (origUserId !== UNAUTHORIZED_USER.userId &&
1783
1810
  context.userId !== origUserId) {
1784
1811
  // User was logged in before, but now logged in as another user.
@@ -6004,7 +6031,7 @@ function LoginDialog({ title, alerts, fields, options, submitLabel, cancelLabel,
6004
6031
  } }))))))),
6005
6032
  _$1("div", { style: Styles.ButtonsDiv },
6006
6033
  _$1(k$1, null,
6007
- hasFields && submitLabel && (_$1("button", { type: "submit", style: Styles.PrimaryButton, onClick: () => onSubmit(params) }, submitLabel)),
6034
+ submitLabel && (hasFields || (!hasOptions && !hasFields)) && (_$1("button", { type: "submit", style: Styles.PrimaryButton, onClick: () => onSubmit(params) }, submitLabel)),
6008
6035
  cancelLabel && (_$1("button", { style: Styles.Button, onClick: onCancel }, cancelLabel))))));
6009
6036
  }
6010
6037
  function valueTransformer(type, value) {
@@ -6857,6 +6884,8 @@ function dexieCloud(dexie) {
6857
6884
  let configuredProgramatically = false;
6858
6885
  // Pending OAuth auth code from dxc-auth redirect (detected in configure())
6859
6886
  let pendingOAuthCode = null;
6887
+ // Pending OAuth error from dxc-auth redirect (detected in configure())
6888
+ let pendingOAuthError = null;
6860
6889
  // local sync worker - used when there's no service worker.
6861
6890
  let localSyncWorker = null;
6862
6891
  dexie.on('ready', (dexie) => __awaiter(this, void 0, void 0, function* () {
@@ -6886,7 +6915,7 @@ function dexieCloud(dexie) {
6886
6915
  const syncComplete = new Subject();
6887
6916
  dexie.cloud = {
6888
6917
  // @ts-ignore
6889
- version: "4.3.2",
6918
+ version: "4.3.4",
6890
6919
  options: Object.assign({}, DEFAULT_OPTIONS),
6891
6920
  schema: null,
6892
6921
  get currentUserId() {
@@ -6936,9 +6965,14 @@ function dexieCloud(dexie) {
6936
6965
  }
6937
6966
  catch (error) {
6938
6967
  // parseOAuthCallback throws OAuthError on error callbacks
6939
- // Store null for code but log the error
6940
- console.warn('[dexie-cloud] OAuth callback error:', error);
6941
6968
  cleanupOAuthUrl();
6969
+ if (error instanceof OAuthError) {
6970
+ pendingOAuthError = error;
6971
+ console.error('[dexie-cloud] OAuth callback error:', error.message);
6972
+ }
6973
+ else {
6974
+ console.error('[dexie-cloud] OAuth callback error:', error);
6975
+ }
6942
6976
  }
6943
6977
  }
6944
6978
  },
@@ -6949,6 +6983,16 @@ function dexieCloud(dexie) {
6949
6983
  : yield logout(DexieCloudDB(dexie));
6950
6984
  });
6951
6985
  },
6986
+ getAuthProviders() {
6987
+ return __awaiter(this, void 0, void 0, function* () {
6988
+ const options = dexie.cloud.options;
6989
+ if (!(options === null || options === void 0 ? void 0 : options.databaseUrl)) {
6990
+ throw new Error('Dexie Cloud not configured. Call db.cloud.configure() first.');
6991
+ }
6992
+ const socialAuthEnabled = options.socialAuth !== false;
6993
+ return fetchAuthProviders(options.databaseUrl, socialAuthEnabled);
6994
+ });
6995
+ },
6952
6996
  sync() {
6953
6997
  return __awaiter(this, arguments, void 0, function* ({ wait, purpose } = { wait: true, purpose: 'push' }) {
6954
6998
  var _a;
@@ -7134,7 +7178,20 @@ function dexieCloud(dexie) {
7134
7178
  }
7135
7179
  // HERE: If requireAuth, do athentication now.
7136
7180
  let changedUser = false;
7137
- const user = yield db.getCurrentUser();
7181
+ let user = yield db.getCurrentUser();
7182
+ // Show pending OAuth error if present (from dxc-auth redirect)
7183
+ if (pendingOAuthError && !db.cloud.isServiceWorkerDB) {
7184
+ const error = pendingOAuthError;
7185
+ pendingOAuthError = null; // Clear pending error
7186
+ console.debug('[dexie-cloud] Showing OAuth error:', error.message);
7187
+ // Show alert to user about the OAuth error
7188
+ yield alertUser(db.cloud.userInteraction, 'Authentication Error', {
7189
+ type: 'error',
7190
+ messageCode: 'GENERIC_ERROR',
7191
+ message: error.message,
7192
+ messageParams: { provider: error.provider || 'unknown' }
7193
+ });
7194
+ }
7138
7195
  // Process pending OAuth callback if present (from dxc-auth redirect)
7139
7196
  if (pendingOAuthCode && !db.cloud.isServiceWorkerDB) {
7140
7197
  const { code, provider } = pendingOAuthCode;
@@ -7142,6 +7199,7 @@ function dexieCloud(dexie) {
7142
7199
  console.debug('[dexie-cloud] Processing OAuth callback, provider:', provider);
7143
7200
  try {
7144
7201
  changedUser = yield login(db, { oauthCode: code, provider });
7202
+ user = yield db.getCurrentUser();
7145
7203
  }
7146
7204
  catch (error) {
7147
7205
  console.error('[dexie-cloud] OAuth login failed:', error);
@@ -7236,7 +7294,7 @@ function dexieCloud(dexie) {
7236
7294
  }
7237
7295
  }
7238
7296
  // @ts-ignore
7239
- dexieCloud.version = "4.3.2";
7297
+ dexieCloud.version = "4.3.4";
7240
7298
  Dexie.Cloud = dexieCloud;
7241
7299
 
7242
7300
  export { dexieCloud as default, defineYDocTrigger, dexieCloud, getTiedObjectId, getTiedRealmId, resolveText };