@sendoracloud/sdk-react-native 0.10.1 → 0.10.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.
package/dist/index.cjs CHANGED
@@ -283,11 +283,29 @@ var Auth = class {
283
283
  return data.user;
284
284
  });
285
285
  }
286
+ /**
287
+ * Create an email/password account.
288
+ *
289
+ * Industry-standard upgrade-first flow (matches Firebase
290
+ * `linkWithCredential`, Auth0 anonymous-merge, Supabase
291
+ * `linkIdentity`): when a refresh token is present in storage we
292
+ * try `/auth-service/upgrade` first — backend validates the
293
+ * refresh, checks the bound user is anonymous, and promotes in
294
+ * place preserving `user.id`. If the bound user is NOT anonymous
295
+ * (already identified, expired refresh, etc.), backend rejects
296
+ * with FORBIDDEN_NON_ANONYMOUS / INVALID_REFRESH and we fall back
297
+ * to a fresh /signup.
298
+ *
299
+ * Why we don't gate on the cached `this.user.isAnonymous`
300
+ * boolean: the in-memory user can be stale during cold start or
301
+ * fast-refresh races. The refresh token in storage is the
302
+ * authoritative signal — if it's there, the upgrade path is
303
+ * worth attempting; backend has the source of truth.
304
+ */
286
305
  signUp(email, password, opts) {
287
306
  return this.serialize(async () => {
288
307
  const refresh = this.hooks.storage.get(REFRESH_KEY);
289
- const isAnonymous = this.user?.isAnonymous === true;
290
- if (isAnonymous && refresh) {
308
+ if (refresh) {
291
309
  try {
292
310
  const data = await this.callAuth("/api/v1/auth-service/upgrade", {
293
311
  refreshToken: refresh,
@@ -301,11 +319,13 @@ var Auth = class {
301
319
  if (err instanceof AuthError && (err.code === "CONFLICT" || err.code === "EMAIL_ALREADY_TAKEN")) {
302
320
  throw new EmailAlreadyTakenError(err.message);
303
321
  }
304
- throw err;
322
+ if (err instanceof AuthError && (err.code === "FORBIDDEN_NON_ANONYMOUS" || err.code === "INVALID_REFRESH" || err.code === "UNAUTHORIZED" || err.code === "REFRESH_EXPIRED")) {
323
+ await this.wipeLocalIdentity();
324
+ } else {
325
+ throw err;
326
+ }
305
327
  }
306
328
  }
307
- const wasIdentified = this.user !== null && this.user.isAnonymous === false;
308
- if (wasIdentified) await this.wipeLocalIdentity();
309
329
  try {
310
330
  const data = await this.callAuth("/api/v1/auth-service/signup", {
311
331
  email,
@@ -770,7 +790,7 @@ function decodeJwtPayload(token) {
770
790
  }
771
791
 
772
792
  // src/index.ts
773
- var SDK_VERSION = "0.10.1";
793
+ var SDK_VERSION = "0.10.2";
774
794
  var DEFAULT_API_URL = "https://api.sendoracloud.com";
775
795
  var ANON_KEY = "anon_id";
776
796
  var USER_ID_KEY = "user_id";
package/dist/index.d.cts CHANGED
@@ -123,6 +123,25 @@ declare class Auth {
123
123
  name?: string;
124
124
  metadata?: Record<string, unknown>;
125
125
  }): Promise<AuthUser>;
126
+ /**
127
+ * Create an email/password account.
128
+ *
129
+ * Industry-standard upgrade-first flow (matches Firebase
130
+ * `linkWithCredential`, Auth0 anonymous-merge, Supabase
131
+ * `linkIdentity`): when a refresh token is present in storage we
132
+ * try `/auth-service/upgrade` first — backend validates the
133
+ * refresh, checks the bound user is anonymous, and promotes in
134
+ * place preserving `user.id`. If the bound user is NOT anonymous
135
+ * (already identified, expired refresh, etc.), backend rejects
136
+ * with FORBIDDEN_NON_ANONYMOUS / INVALID_REFRESH and we fall back
137
+ * to a fresh /signup.
138
+ *
139
+ * Why we don't gate on the cached `this.user.isAnonymous`
140
+ * boolean: the in-memory user can be stale during cold start or
141
+ * fast-refresh races. The refresh token in storage is the
142
+ * authoritative signal — if it's there, the upgrade path is
143
+ * worth attempting; backend has the source of truth.
144
+ */
126
145
  signUp(email: string, password: string, opts?: {
127
146
  name?: string;
128
147
  metadata?: Record<string, unknown>;
package/dist/index.d.ts CHANGED
@@ -123,6 +123,25 @@ declare class Auth {
123
123
  name?: string;
124
124
  metadata?: Record<string, unknown>;
125
125
  }): Promise<AuthUser>;
126
+ /**
127
+ * Create an email/password account.
128
+ *
129
+ * Industry-standard upgrade-first flow (matches Firebase
130
+ * `linkWithCredential`, Auth0 anonymous-merge, Supabase
131
+ * `linkIdentity`): when a refresh token is present in storage we
132
+ * try `/auth-service/upgrade` first — backend validates the
133
+ * refresh, checks the bound user is anonymous, and promotes in
134
+ * place preserving `user.id`. If the bound user is NOT anonymous
135
+ * (already identified, expired refresh, etc.), backend rejects
136
+ * with FORBIDDEN_NON_ANONYMOUS / INVALID_REFRESH and we fall back
137
+ * to a fresh /signup.
138
+ *
139
+ * Why we don't gate on the cached `this.user.isAnonymous`
140
+ * boolean: the in-memory user can be stale during cold start or
141
+ * fast-refresh races. The refresh token in storage is the
142
+ * authoritative signal — if it's there, the upgrade path is
143
+ * worth attempting; backend has the source of truth.
144
+ */
126
145
  signUp(email: string, password: string, opts?: {
127
146
  name?: string;
128
147
  metadata?: Record<string, unknown>;
package/dist/index.js CHANGED
@@ -243,11 +243,29 @@ var Auth = class {
243
243
  return data.user;
244
244
  });
245
245
  }
246
+ /**
247
+ * Create an email/password account.
248
+ *
249
+ * Industry-standard upgrade-first flow (matches Firebase
250
+ * `linkWithCredential`, Auth0 anonymous-merge, Supabase
251
+ * `linkIdentity`): when a refresh token is present in storage we
252
+ * try `/auth-service/upgrade` first — backend validates the
253
+ * refresh, checks the bound user is anonymous, and promotes in
254
+ * place preserving `user.id`. If the bound user is NOT anonymous
255
+ * (already identified, expired refresh, etc.), backend rejects
256
+ * with FORBIDDEN_NON_ANONYMOUS / INVALID_REFRESH and we fall back
257
+ * to a fresh /signup.
258
+ *
259
+ * Why we don't gate on the cached `this.user.isAnonymous`
260
+ * boolean: the in-memory user can be stale during cold start or
261
+ * fast-refresh races. The refresh token in storage is the
262
+ * authoritative signal — if it's there, the upgrade path is
263
+ * worth attempting; backend has the source of truth.
264
+ */
246
265
  signUp(email, password, opts) {
247
266
  return this.serialize(async () => {
248
267
  const refresh = this.hooks.storage.get(REFRESH_KEY);
249
- const isAnonymous = this.user?.isAnonymous === true;
250
- if (isAnonymous && refresh) {
268
+ if (refresh) {
251
269
  try {
252
270
  const data = await this.callAuth("/api/v1/auth-service/upgrade", {
253
271
  refreshToken: refresh,
@@ -261,11 +279,13 @@ var Auth = class {
261
279
  if (err instanceof AuthError && (err.code === "CONFLICT" || err.code === "EMAIL_ALREADY_TAKEN")) {
262
280
  throw new EmailAlreadyTakenError(err.message);
263
281
  }
264
- throw err;
282
+ if (err instanceof AuthError && (err.code === "FORBIDDEN_NON_ANONYMOUS" || err.code === "INVALID_REFRESH" || err.code === "UNAUTHORIZED" || err.code === "REFRESH_EXPIRED")) {
283
+ await this.wipeLocalIdentity();
284
+ } else {
285
+ throw err;
286
+ }
265
287
  }
266
288
  }
267
- const wasIdentified = this.user !== null && this.user.isAnonymous === false;
268
- if (wasIdentified) await this.wipeLocalIdentity();
269
289
  try {
270
290
  const data = await this.callAuth("/api/v1/auth-service/signup", {
271
291
  email,
@@ -730,7 +750,7 @@ function decodeJwtPayload(token) {
730
750
  }
731
751
 
732
752
  // src/index.ts
733
- var SDK_VERSION = "0.10.1";
753
+ var SDK_VERSION = "0.10.2";
734
754
  var DEFAULT_API_URL = "https://api.sendoracloud.com";
735
755
  var ANON_KEY = "anon_id";
736
756
  var USER_ID_KEY = "user_id";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendoracloud/sdk-react-native",
3
- "version": "0.10.1",
3
+ "version": "0.10.2",
4
4
  "description": "Sendora Cloud React Native + Expo SDK — analytics, identity, push-token registration, auth. Expo Go compatible, no native modules beyond AsyncStorage.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",