@layers/amba-react-native 4.0.4 → 4.0.6

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
@@ -831,6 +831,18 @@ const notifications = {
831
831
  //#region src/realtime.ts
832
832
  const INITIAL_BACKOFF_MS = 500;
833
833
  const MAX_BACKOFF_MS = 15e3;
834
+ const DEFAULT_API_URL = "https://api.amba.dev";
835
+ const DEFAULT_REALTIME_URL = "https://realtime.amba.host";
836
+ /**
837
+ * Resolve the base URL for live-update connections. Long-lived streams are
838
+ * served from a dedicated realtime host (no request-duration ceiling), so
839
+ * when the SDK points at the default production API the stream connects
840
+ * there instead. Any custom `apiUrl` (dev, staging, self-hosted, proxy)
841
+ * keeps live updates on that same origin — override semantics unchanged.
842
+ */
843
+ function resolveRealtimeBaseUrl(baseUrl) {
844
+ return baseUrl.replace(/\/+$/, "") === DEFAULT_API_URL ? DEFAULT_REALTIME_URL : baseUrl;
845
+ }
834
846
  /**
835
847
  * Builder returned by `Amba.collection(name)`. Chain `.where(...)` to
836
848
  * narrow, then `.subscribe(cb)` to start streaming.
@@ -854,7 +866,7 @@ var CollectionSubscription = class {
854
866
  if (!this.ctx.authHeaders()) throw new Error("Amba.collection().subscribe() requires an authenticated session — call Amba.auth.signIn* first.");
855
867
  const qs = Object.keys(this.filters).length > 0 ? `?where=${encodeURIComponent(JSON.stringify(this.filters))}` : "";
856
868
  return openStream({
857
- url: `${this.ctx.baseUrl}/v1/client/realtime/collections/${encodeURIComponent(this.name)}${qs}`,
869
+ url: `${resolveRealtimeBaseUrl(this.ctx.baseUrl)}/v1/client/realtime/collections/${encodeURIComponent(this.name)}${qs}`,
858
870
  authHeaders: this.ctx.authHeaders,
859
871
  onChange: cb,
860
872
  options,
@@ -878,7 +890,7 @@ var ConversationSubscription = class {
878
890
  subscribe(cb, options = {}) {
879
891
  if (!this.ctx.authHeaders()) throw new Error("Amba.messaging.conversation().subscribe() requires an authenticated session — call Amba.auth.signIn* first.");
880
892
  return openStream({
881
- url: `${this.ctx.baseUrl}/v1/client/realtime/conversations/${encodeURIComponent(this.conversationId)}`,
893
+ url: `${resolveRealtimeBaseUrl(this.ctx.baseUrl)}/v1/client/realtime/conversations/${encodeURIComponent(this.conversationId)}`,
882
894
  authHeaders: this.ctx.authHeaders,
883
895
  onChange: cb,
884
896
  options,
@@ -902,7 +914,7 @@ var GamificationSubscription = class {
902
914
  subscribe(cb, options = {}) {
903
915
  if (!this.ctx.authHeaders()) throw new Error("Amba.gamification.subscribe() requires an authenticated session — call Amba.auth.signIn* first.");
904
916
  return openStream({
905
- url: `${this.ctx.baseUrl}/v1/client/realtime/gamification`,
917
+ url: `${resolveRealtimeBaseUrl(this.ctx.baseUrl)}/v1/client/realtime/gamification`,
906
918
  authHeaders: this.ctx.authHeaders,
907
919
  onChange: cb,
908
920
  options,
@@ -1138,7 +1150,7 @@ function delay(ms) {
1138
1150
  //#endregion
1139
1151
  //#region src/index.ts
1140
1152
  /** SDK semver — burned in at build time. Bump alongside package.json. */
1141
- const SDK_VERSION = "4.0.4";
1153
+ const SDK_VERSION = "4.0.6";
1142
1154
  /**
1143
1155
  * Default host that serves deployed functions. `Amba.functions.invoke(name)`
1144
1156
  * targets `https://<projectId>.<DEFAULT_FUNCTIONS_HOST>/<name>` unless a
@@ -1190,6 +1202,58 @@ function notifyAuthSubscribers(session) {
1190
1202
  if (typeof console !== "undefined") console.error("[amba] onAuthStateChange callback threw", err);
1191
1203
  }
1192
1204
  }
1205
+ function normalizeAuthProviderNames(value) {
1206
+ let normalized = value;
1207
+ if (typeof normalized === "string") {
1208
+ const trimmed = normalized.trim();
1209
+ if (trimmed === "") return [];
1210
+ try {
1211
+ normalized = JSON.parse(trimmed);
1212
+ } catch {
1213
+ return [normalized];
1214
+ }
1215
+ }
1216
+ if (!Array.isArray(normalized)) {
1217
+ if (normalized && typeof normalized === "object") {
1218
+ const provider = normalized.provider;
1219
+ return typeof provider === "string" ? [provider] : [];
1220
+ }
1221
+ return [];
1222
+ }
1223
+ return normalized.map((entry) => {
1224
+ if (typeof entry === "string") {
1225
+ const trimmed = entry.trim();
1226
+ if (trimmed === "") return null;
1227
+ try {
1228
+ const parsed = JSON.parse(trimmed);
1229
+ if (parsed && typeof parsed === "object") {
1230
+ const provider = parsed.provider;
1231
+ return typeof provider === "string" ? provider : null;
1232
+ }
1233
+ } catch {
1234
+ return entry;
1235
+ }
1236
+ return entry;
1237
+ }
1238
+ if (entry && typeof entry === "object") {
1239
+ const provider = entry.provider;
1240
+ return typeof provider === "string" ? provider : null;
1241
+ }
1242
+ return null;
1243
+ }).filter((provider) => provider !== null);
1244
+ }
1245
+ function normalizeUserAuthProviders(user) {
1246
+ return {
1247
+ ...user,
1248
+ auth_providers: normalizeAuthProviderNames(user.auth_providers)
1249
+ };
1250
+ }
1251
+ function normalizeAuthResult(result) {
1252
+ return {
1253
+ ...result,
1254
+ user: normalizeUserAuthProviders(result.user)
1255
+ };
1256
+ }
1193
1257
  async function notifyFromAuthResult(result) {
1194
1258
  notifyAuthSubscribers(snapshotSession(result.user));
1195
1259
  }
@@ -1206,7 +1270,7 @@ async function persistAuthResult(state_, result) {
1206
1270
  });
1207
1271
  }
1208
1272
  async function postAuth(state_, path, body) {
1209
- const result = (await state_.http.post(path, body)).data;
1273
+ const result = normalizeAuthResult((await state_.http.post(path, body)).data);
1210
1274
  await persistAuthResult(state_, result);
1211
1275
  await notifyFromAuthResult(result);
1212
1276
  return result;
@@ -1238,7 +1302,7 @@ async function ensureAnonymousSession(state_) {
1238
1302
  * Existing session tokens stay untouched.
1239
1303
  */
1240
1304
  async function postUserNoRotate(state_, path, body) {
1241
- return (await state_.http.post(path, body)).data.user;
1305
+ return normalizeUserAuthProviders((await state_.http.post(path, body)).data.user);
1242
1306
  }
1243
1307
  async function unlinkProvider(provider) {
1244
1308
  const s = getState();
@@ -1298,6 +1362,8 @@ const P = {
1298
1362
  pushSubscribe: "/v1/client/push/subscribe",
1299
1363
  pushUnsubscribe: "/v1/client/push/unsubscribe",
1300
1364
  entitlements: "/v1/client/entitlements",
1365
+ entitlementsRestore: "/v1/client/entitlements/restore",
1366
+ offerings: "/v1/client/offerings",
1301
1367
  aiAnthropic: "/v1/client/ai/anthropic/messages",
1302
1368
  config: "/v1/client/config",
1303
1369
  flags: "/v1/client/flags",
@@ -1652,12 +1718,12 @@ const Amba = {
1652
1718
  me: async () => {
1653
1719
  const s = getState();
1654
1720
  if (!s.identity.getSessionToken()) throw new AmbaApiError("UNAUTHORIZED", "no active session — call signIn* first");
1655
- return (await s.http.get(P.authMe)).data;
1721
+ return normalizeUserAuthProviders((await s.http.get(P.authMe)).data);
1656
1722
  },
1657
1723
  getSession: async () => {
1658
1724
  const s = getState();
1659
1725
  if (!s.identity.isAuthenticated()) return null;
1660
- return snapshotSession((await s.http.get(P.authMe)).data);
1726
+ return snapshotSession(normalizeUserAuthProviders((await s.http.get(P.authMe)).data));
1661
1727
  },
1662
1728
  getAnonymousId: async () => {
1663
1729
  return getState().identity.getAnonymousId();
@@ -1674,7 +1740,7 @@ const Amba = {
1674
1740
  const s = getState();
1675
1741
  if (!s.identity.getSessionToken()) throw new AmbaApiError("UNAUTHORIZED", "no active session — call signIn* first");
1676
1742
  const path = userId === void 0 || userId === null || userId === "" ? P.usersMe : `${P.users}/${encodePathSegment(userId)}`;
1677
- return (await s.http.get(path)).data;
1743
+ return normalizeUserAuthProviders((await s.http.get(path)).data);
1678
1744
  },
1679
1745
  update: ((arg1, arg2) => {
1680
1746
  const s = getState();
@@ -1682,7 +1748,7 @@ const Amba = {
1682
1748
  const isById = typeof arg1 === "string";
1683
1749
  const patch = isById ? arg2 ?? {} : arg1;
1684
1750
  const path = isById ? `${P.users}/${encodePathSegment(arg1)}` : `${P.users}/me`;
1685
- return s.http.patch(path, patch).then((env) => env.data);
1751
+ return s.http.patch(path, patch).then((env) => normalizeUserAuthProviders(env.data));
1686
1752
  })
1687
1753
  },
1688
1754
  sessions: {
@@ -1960,8 +2026,14 @@ const Amba = {
1960
2026
  },
1961
2027
  has: async (entitlementId) => {
1962
2028
  return (await getState().http.get(P.entitlements, { active_only: "true" })).data.some((e) => e.entitlement_id === entitlementId && e.is_active);
2029
+ },
2030
+ restore: async () => {
2031
+ return (await getState().http.post(P.entitlementsRestore, {})).data;
1963
2032
  }
1964
2033
  },
2034
+ offerings: async (offeringId) => {
2035
+ return (await getState().http.get(P.offerings, offeringId ? { offering_id: offeringId } : void 0)).data;
2036
+ },
1965
2037
  ai: { anthropic: { messages: { create: async (request) => {
1966
2038
  const env = await getState().http.post(P.aiAnthropic, request);
1967
2039
  if (!env || typeof env !== "object" || env.data === void 0) throw new AmbaApiError("MALFORMED_RESPONSE", "AI response was not wrapped in the expected { data } envelope", env);