@layers/amba-react-native 1.0.1 → 2.1.0
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/README.md +16 -18
- package/dist/index.cjs +708 -178
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +88 -49
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +88 -49
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +708 -178
- package/dist/index.js.map +1 -1
- package/package.json +2 -5
package/dist/index.js
CHANGED
|
@@ -52,70 +52,283 @@ var AmbaApiError = class extends Error {
|
|
|
52
52
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
53
53
|
}
|
|
54
54
|
};
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
55
|
+
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/http-client.ts
|
|
58
|
+
/** Default amba API endpoint. */
|
|
59
|
+
const DEFAULT_BASE_URL = "https://api.amba.dev";
|
|
60
|
+
var AmbaHttpClient = class {
|
|
61
|
+
apiKey;
|
|
62
|
+
baseUrl;
|
|
63
|
+
sdkPlatform;
|
|
64
|
+
sdkVersionHeader;
|
|
65
|
+
identity;
|
|
66
|
+
fetchImpl;
|
|
67
|
+
constructor(opts) {
|
|
68
|
+
this.apiKey = opts.apiKey;
|
|
69
|
+
this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
|
|
70
|
+
this.sdkPlatform = opts.sdkPlatform;
|
|
71
|
+
this.sdkVersionHeader = opts.sdkVersionHeader;
|
|
72
|
+
this.identity = opts.identity;
|
|
73
|
+
this.fetchImpl = opts.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
74
|
+
}
|
|
75
|
+
/** Build the absolute URL for an API path, optionally with a query string. */
|
|
76
|
+
url(path, query) {
|
|
77
|
+
const base = this.baseUrl + path;
|
|
78
|
+
if (!query) return base;
|
|
79
|
+
const parts = [];
|
|
80
|
+
for (const k of Object.keys(query)) {
|
|
81
|
+
const v = query[k];
|
|
82
|
+
if (v === void 0 || v === null) continue;
|
|
83
|
+
parts.push(`${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);
|
|
84
|
+
}
|
|
85
|
+
if (parts.length === 0) return base;
|
|
86
|
+
return `${base}?${parts.join("&")}`;
|
|
87
|
+
}
|
|
88
|
+
/** Build the standard request headers for the current identity state. */
|
|
89
|
+
buildHeaders(extra) {
|
|
90
|
+
const h = {
|
|
91
|
+
"X-Api-Key": this.apiKey,
|
|
92
|
+
"X-SDK-Platform": this.sdkPlatform,
|
|
93
|
+
"X-SDK-Version": this.sdkVersionHeader,
|
|
94
|
+
"Content-Type": "application/json",
|
|
95
|
+
Accept: "application/json",
|
|
96
|
+
...extra
|
|
97
|
+
};
|
|
98
|
+
const tok = this.identity.getSessionToken();
|
|
99
|
+
if (tok) h.Authorization = `Bearer ${tok}`;
|
|
100
|
+
return h;
|
|
101
|
+
}
|
|
102
|
+
async get(path, query) {
|
|
103
|
+
return this.request("GET", path, void 0, query);
|
|
104
|
+
}
|
|
105
|
+
async post(path, body, query) {
|
|
106
|
+
return this.request("POST", path, body, query);
|
|
107
|
+
}
|
|
108
|
+
async patch(path, body) {
|
|
109
|
+
return this.request("PATCH", path, body);
|
|
110
|
+
}
|
|
111
|
+
async delete(path) {
|
|
112
|
+
return this.request("DELETE", path);
|
|
113
|
+
}
|
|
114
|
+
async request(method, path, body, query) {
|
|
115
|
+
const url = this.url(path, query);
|
|
116
|
+
const init = {
|
|
117
|
+
method,
|
|
118
|
+
headers: this.buildHeaders(),
|
|
119
|
+
body: body === void 0 ? void 0 : JSON.stringify(body ?? {})
|
|
120
|
+
};
|
|
121
|
+
let response;
|
|
122
|
+
try {
|
|
123
|
+
response = await this.fetchImpl(url, init);
|
|
124
|
+
} catch (err) {
|
|
125
|
+
throw new AmbaApiError("NETWORK_ERROR", `Network error: ${err instanceof Error ? err.message : typeof err === "string" ? err : "fetch failed"}`, err);
|
|
126
|
+
}
|
|
127
|
+
return parseResponse(response);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
async function parseResponse(response) {
|
|
131
|
+
if (response.status === 204) return null;
|
|
132
|
+
const text = await response.text();
|
|
133
|
+
const isJson = (response.headers.get("content-type") ?? "").includes("application/json");
|
|
134
|
+
if (!response.ok) throw mapHttpError(response, text, isJson);
|
|
135
|
+
if (text === "") return null;
|
|
136
|
+
if (isJson) try {
|
|
137
|
+
return JSON.parse(text);
|
|
138
|
+
} catch (err) {
|
|
139
|
+
throw new AmbaApiError("HTTP_ERROR", `Failed to parse JSON response: ${err instanceof Error ? err.message : String(err)}`, { body: text });
|
|
140
|
+
}
|
|
141
|
+
return text;
|
|
142
|
+
}
|
|
143
|
+
function mapHttpError(response, rawBody, isJson) {
|
|
144
|
+
let parsed = void 0;
|
|
145
|
+
let message = rawBody || `HTTP ${response.status}`;
|
|
146
|
+
let code;
|
|
147
|
+
let envelopeDetails = void 0;
|
|
148
|
+
let envelopeRecognized = false;
|
|
149
|
+
if (isJson && rawBody) {
|
|
150
|
+
try {
|
|
151
|
+
parsed = JSON.parse(rawBody);
|
|
152
|
+
} catch {
|
|
153
|
+
parsed = void 0;
|
|
154
|
+
}
|
|
155
|
+
const innerErr = parsed?.error;
|
|
156
|
+
if (innerErr && typeof innerErr === "object" && typeof innerErr.code === "string") {
|
|
157
|
+
code = innerErr.code;
|
|
158
|
+
envelopeRecognized = true;
|
|
159
|
+
envelopeDetails = innerErr.details;
|
|
160
|
+
if (typeof innerErr.message === "string") message = innerErr.message;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const details = envelopeRecognized ? envelopeDetails : parsed;
|
|
164
|
+
switch (response.status) {
|
|
165
|
+
case 401: return new AmbaApiError(code ?? "UNAUTHORIZED", message, details);
|
|
166
|
+
case 403: return new AmbaApiError(code ?? "FORBIDDEN", message, details);
|
|
167
|
+
case 404: return new AmbaApiError(code ?? "NOT_FOUND", message, details);
|
|
168
|
+
case 409: return new AmbaApiError(code ?? "CONFLICT", message, details);
|
|
169
|
+
case 422: return new AmbaApiError(code ?? "VALIDATION_ERROR", message, details);
|
|
170
|
+
case 429: {
|
|
171
|
+
const retryAfter = response.headers.get("retry-after") ?? "unspecified";
|
|
172
|
+
const serverGaveMessage = message !== rawBody && message !== `HTTP ${response.status}`;
|
|
173
|
+
return new AmbaApiError(code ?? "RATE_LIMITED", serverGaveMessage ? message : `Rate limited (retry after ${retryAfter})`, details);
|
|
174
|
+
}
|
|
175
|
+
default: return new AmbaApiError(code ?? "HTTP_ERROR", message, envelopeRecognized ? envelopeDetails : parsed ?? {
|
|
176
|
+
status: response.status,
|
|
177
|
+
body: rawBody
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
//#endregion
|
|
183
|
+
//#region src/uuid.ts
|
|
184
|
+
function getRandomBytes(n) {
|
|
185
|
+
const bytes = new Uint8Array(n);
|
|
186
|
+
if (typeof globalThis.crypto?.getRandomValues === "function") {
|
|
187
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
188
|
+
return bytes;
|
|
189
|
+
}
|
|
190
|
+
for (let i = 0; i < n; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
191
|
+
return bytes;
|
|
80
192
|
}
|
|
81
193
|
/**
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* without re-wrapping.
|
|
85
|
-
*
|
|
86
|
-
* Heuristics:
|
|
87
|
-
* 1. Existing `AmbaApiError` → returned as-is.
|
|
88
|
-
* 2. Object with `.code` → use that code + `.message` + `.details`.
|
|
89
|
-
* 3. `Error` (e.g. WASM JsError) → infer code from message prefix.
|
|
90
|
-
* 4. Anything else → stringify and tag UNKNOWN_ERROR.
|
|
194
|
+
* Generate a UUID v4 (random) per RFC 4122. Wire shape:
|
|
195
|
+
* `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx` where `y ∈ {8,9,a,b}`.
|
|
91
196
|
*/
|
|
92
|
-
function
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (err instanceof Error) return new AmbaApiError(codeFromMessage(err.message), err.message);
|
|
99
|
-
return new AmbaApiError("UNKNOWN_ERROR", String(err));
|
|
197
|
+
function uuidv4() {
|
|
198
|
+
const b = getRandomBytes(16);
|
|
199
|
+
b[6] = b[6] & 15 | 64;
|
|
200
|
+
b[8] = b[8] & 63 | 128;
|
|
201
|
+
const h = Array.from(b, (v) => v.toString(16).padStart(2, "0"));
|
|
202
|
+
return h.slice(0, 4).join("") + "-" + h.slice(4, 6).join("") + "-" + h.slice(6, 8).join("") + "-" + h.slice(8, 10).join("") + "-" + h.slice(10, 16).join("");
|
|
100
203
|
}
|
|
101
204
|
|
|
102
205
|
//#endregion
|
|
103
|
-
//#region src/
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return
|
|
206
|
+
//#region src/identity.ts
|
|
207
|
+
/** Storage key under the configured prefix. Bumped when shape changes. */
|
|
208
|
+
const IDENTITY_KEY = "identity_v1";
|
|
209
|
+
function isExpired(expiresAt) {
|
|
210
|
+
if (!expiresAt) return false;
|
|
211
|
+
const t = Date.parse(expiresAt);
|
|
212
|
+
if (Number.isNaN(t)) return false;
|
|
213
|
+
return t <= Date.now();
|
|
108
214
|
}
|
|
109
215
|
/**
|
|
110
|
-
*
|
|
111
|
-
*
|
|
216
|
+
* The SDK's identity state — held in memory after hydration, persisted
|
|
217
|
+
* to AsyncStorage on every mutation. All methods are sync after
|
|
218
|
+
* `hydrate()` resolves.
|
|
112
219
|
*/
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
220
|
+
var IdentityStore = class IdentityStore {
|
|
221
|
+
anonymousId;
|
|
222
|
+
appUserId = null;
|
|
223
|
+
sessionToken = null;
|
|
224
|
+
refreshToken = null;
|
|
225
|
+
expiresAt = null;
|
|
226
|
+
constructor(persistence, seed) {
|
|
227
|
+
this.persistence = persistence;
|
|
228
|
+
this.anonymousId = seed.anonymous_id;
|
|
229
|
+
this.appUserId = seed.app_user_id;
|
|
230
|
+
this.sessionToken = seed.session_token;
|
|
231
|
+
this.refreshToken = seed.refresh_token;
|
|
232
|
+
this.expiresAt = seed.expires_at;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Build a store, hydrating any prior identity from AsyncStorage.
|
|
236
|
+
* If no prior identity exists, a fresh `anonymous_id` is minted.
|
|
237
|
+
* Expired session tokens are dropped at hydration (the refresh token
|
|
238
|
+
* survives so callers can `auth.refresh()` to mint a new pair).
|
|
239
|
+
*/
|
|
240
|
+
static async create(persistence) {
|
|
241
|
+
const raw = await persistence.read(IDENTITY_KEY);
|
|
242
|
+
let seed;
|
|
243
|
+
if (raw) try {
|
|
244
|
+
const parsed = JSON.parse(raw);
|
|
245
|
+
seed = {
|
|
246
|
+
anonymous_id: parsed.anonymous_id ?? uuidv4(),
|
|
247
|
+
app_user_id: parsed.app_user_id ?? null,
|
|
248
|
+
session_token: parsed.session_token ?? null,
|
|
249
|
+
refresh_token: parsed.refresh_token ?? null,
|
|
250
|
+
expires_at: parsed.expires_at ?? null
|
|
251
|
+
};
|
|
252
|
+
} catch {
|
|
253
|
+
seed = freshSeed();
|
|
254
|
+
}
|
|
255
|
+
else seed = freshSeed();
|
|
256
|
+
if (isExpired(seed.expires_at)) {
|
|
257
|
+
seed.session_token = null;
|
|
258
|
+
seed.expires_at = null;
|
|
259
|
+
}
|
|
260
|
+
const store = new IdentityStore(persistence, seed);
|
|
261
|
+
await store.flush();
|
|
262
|
+
return store;
|
|
118
263
|
}
|
|
264
|
+
snapshot() {
|
|
265
|
+
return {
|
|
266
|
+
anonymousId: this.anonymousId,
|
|
267
|
+
appUserId: this.appUserId,
|
|
268
|
+
sessionToken: this.sessionToken,
|
|
269
|
+
refreshToken: this.refreshToken,
|
|
270
|
+
expiresAt: this.expiresAt
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
getAnonymousId() {
|
|
274
|
+
return this.anonymousId;
|
|
275
|
+
}
|
|
276
|
+
getAppUserId() {
|
|
277
|
+
return this.appUserId;
|
|
278
|
+
}
|
|
279
|
+
getSessionToken() {
|
|
280
|
+
return this.sessionToken;
|
|
281
|
+
}
|
|
282
|
+
getRefreshToken() {
|
|
283
|
+
return this.refreshToken;
|
|
284
|
+
}
|
|
285
|
+
isAuthenticated() {
|
|
286
|
+
return this.sessionToken !== null;
|
|
287
|
+
}
|
|
288
|
+
async setSession(args) {
|
|
289
|
+
this.appUserId = args.appUserId;
|
|
290
|
+
this.sessionToken = args.sessionToken;
|
|
291
|
+
this.refreshToken = args.refreshToken;
|
|
292
|
+
this.expiresAt = args.expiresAt;
|
|
293
|
+
await this.flush();
|
|
294
|
+
}
|
|
295
|
+
async clearSession(rotateAnonymousId) {
|
|
296
|
+
this.appUserId = null;
|
|
297
|
+
this.sessionToken = null;
|
|
298
|
+
this.refreshToken = null;
|
|
299
|
+
this.expiresAt = null;
|
|
300
|
+
if (rotateAnonymousId) this.anonymousId = uuidv4();
|
|
301
|
+
await this.flush();
|
|
302
|
+
}
|
|
303
|
+
async flush() {
|
|
304
|
+
const payload = {
|
|
305
|
+
anonymous_id: this.anonymousId,
|
|
306
|
+
app_user_id: this.appUserId,
|
|
307
|
+
session_token: this.sessionToken,
|
|
308
|
+
refresh_token: this.refreshToken,
|
|
309
|
+
expires_at: this.expiresAt
|
|
310
|
+
};
|
|
311
|
+
await this.persistence.write(IDENTITY_KEY, JSON.stringify(payload));
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
function freshSeed() {
|
|
315
|
+
return {
|
|
316
|
+
anonymous_id: uuidv4(),
|
|
317
|
+
app_user_id: null,
|
|
318
|
+
session_token: null,
|
|
319
|
+
refresh_token: null,
|
|
320
|
+
expires_at: null
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
//#endregion
|
|
325
|
+
//#region src/index.ts
|
|
326
|
+
/** SDK semver — burned in at build time. Bump alongside package.json. */
|
|
327
|
+
const SDK_VERSION = "2.1.0";
|
|
328
|
+
let state = null;
|
|
329
|
+
function getState() {
|
|
330
|
+
if (!state) throw new AmbaApiError("NOT_INITIALIZED", "@layers/amba-react-native: SDK not configured. Call Amba.configure({ apiKey }) at app start.");
|
|
331
|
+
return state;
|
|
119
332
|
}
|
|
120
333
|
const authSubscribers = /* @__PURE__ */ new Set();
|
|
121
334
|
function snapshotSession(user) {
|
|
@@ -137,82 +350,183 @@ function notifyAuthSubscribers(session) {
|
|
|
137
350
|
async function notifyFromAuthResult(result) {
|
|
138
351
|
notifyAuthSubscribers(snapshotSession(result.user));
|
|
139
352
|
}
|
|
353
|
+
function expiresAtFromResult(result) {
|
|
354
|
+
if (result.expires_at && typeof result.expires_at === "string") return result.expires_at;
|
|
355
|
+
return new Date(Date.now() + 3600 * 1e3).toISOString();
|
|
356
|
+
}
|
|
357
|
+
async function persistAuthResult(state_, result) {
|
|
358
|
+
await state_.identity.setSession({
|
|
359
|
+
appUserId: result.user.id,
|
|
360
|
+
sessionToken: result.session_token,
|
|
361
|
+
refreshToken: result.refresh_token,
|
|
362
|
+
expiresAt: expiresAtFromResult(result)
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
async function postAuth(state_, path, body) {
|
|
366
|
+
const result = (await state_.http.post(path, body)).data;
|
|
367
|
+
await persistAuthResult(state_, result);
|
|
368
|
+
await notifyFromAuthResult(result);
|
|
369
|
+
return result;
|
|
370
|
+
}
|
|
371
|
+
function requireEmail(email) {
|
|
372
|
+
const trimmed = email.trim();
|
|
373
|
+
if (trimmed === "") throw new AmbaApiError("VALIDATION_ERROR", "email must not be empty");
|
|
374
|
+
if (!trimmed.includes("@") || !trimmed.includes(".")) throw new AmbaApiError("VALIDATION_ERROR", `email '${email}' is not a valid email address`);
|
|
375
|
+
}
|
|
376
|
+
function requirePassword(password) {
|
|
377
|
+
if (password.length < 8) throw new AmbaApiError("VALIDATION_ERROR", "password must be at least 8 characters");
|
|
378
|
+
}
|
|
379
|
+
const P = {
|
|
380
|
+
authAnonymous: "/v1/client/auth/anonymous",
|
|
381
|
+
authEmailLogin: "/v1/client/auth/email/login",
|
|
382
|
+
authEmailSignup: "/v1/client/auth/email/signup",
|
|
383
|
+
authSocial: "/v1/client/auth/social",
|
|
384
|
+
authRefresh: "/v1/client/auth/refresh",
|
|
385
|
+
authLogout: "/v1/client/auth/logout",
|
|
386
|
+
authMe: "/v1/client/auth/me",
|
|
387
|
+
events: "/v1/client/events",
|
|
388
|
+
collections: "/v1/client/collections",
|
|
389
|
+
storage: "/v1/client/media",
|
|
390
|
+
storagePresign: "/v1/client/media/presign",
|
|
391
|
+
storageCommit: "/v1/client/media/commit",
|
|
392
|
+
push: "/v1/client/push",
|
|
393
|
+
pushRegister: "/v1/client/push/register",
|
|
394
|
+
pushUnregister: "/v1/client/push/unregister",
|
|
395
|
+
pushTokens: "/v1/client/push/tokens",
|
|
396
|
+
pushSubscribe: "/v1/client/push/subscribe",
|
|
397
|
+
pushUnsubscribe: "/v1/client/push/unsubscribe",
|
|
398
|
+
entitlements: "/v1/client/entitlements",
|
|
399
|
+
aiAnthropic: "/v1/client/ai/anthropic/messages",
|
|
400
|
+
config: "/v1/client/config",
|
|
401
|
+
flags: "/v1/client/flags",
|
|
402
|
+
achievements: "/v1/client/achievements",
|
|
403
|
+
challenges: "/v1/client/challenges",
|
|
404
|
+
catalog: "/v1/client/catalog",
|
|
405
|
+
currencies: "/v1/client/currencies",
|
|
406
|
+
inventory: "/v1/client/inventory",
|
|
407
|
+
leaderboards: "/v1/client/leaderboards",
|
|
408
|
+
stores: "/v1/client/stores",
|
|
409
|
+
xp: "/v1/client/xp",
|
|
410
|
+
streaks: "/v1/client/streaks",
|
|
411
|
+
feeds: "/v1/client/feeds",
|
|
412
|
+
friends: "/v1/client/friends",
|
|
413
|
+
groups: "/v1/client/groups",
|
|
414
|
+
messaging: "/v1/client/messaging",
|
|
415
|
+
moderation: "/v1/client/moderation",
|
|
416
|
+
reviews: "/v1/client/reviews",
|
|
417
|
+
roles: "/v1/client/roles",
|
|
418
|
+
referrals: "/v1/client/referrals",
|
|
419
|
+
content: "/v1/client/content",
|
|
420
|
+
deepLinks: "/v1/client/deeplinks",
|
|
421
|
+
onboarding: "/v1/client/onboarding"
|
|
422
|
+
};
|
|
423
|
+
function encodePathSegment(s) {
|
|
424
|
+
return encodeURIComponent(s);
|
|
425
|
+
}
|
|
140
426
|
/**
|
|
141
427
|
* Top-level SDK namespace for React Native.
|
|
142
|
-
*
|
|
143
|
-
* Differences from `@layers/amba-web`:
|
|
144
|
-
* - `configure` is async (waits for WASM init AND AsyncStorage hydration).
|
|
145
|
-
* - Persistence is AsyncStorage-backed — identity survives app restarts.
|
|
146
428
|
*/
|
|
147
429
|
const Amba = {
|
|
148
430
|
configure: async (config) => {
|
|
149
431
|
if (!config.apiKey || config.apiKey.trim() === "") throw new Error("Amba.configure: apiKey is required");
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
432
|
+
const persistence = new AsyncStoragePersistence(config.storagePrefix ?? "amba");
|
|
433
|
+
await persistence.hydrate();
|
|
434
|
+
const identity = await IdentityStore.create(persistence);
|
|
435
|
+
const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
436
|
+
const sdkVersionHeader = `amba-sdk/${SDK_VERSION} ${`amba-react-native/${SDK_VERSION}`}`;
|
|
437
|
+
state = {
|
|
438
|
+
http: new AmbaHttpClient({
|
|
439
|
+
apiKey: config.apiKey,
|
|
440
|
+
baseUrl,
|
|
441
|
+
sdkPlatform: "react-native",
|
|
442
|
+
sdkVersionHeader,
|
|
443
|
+
identity
|
|
444
|
+
}),
|
|
445
|
+
identity,
|
|
446
|
+
debug: config.debug ?? false
|
|
447
|
+
};
|
|
448
|
+
},
|
|
449
|
+
_reset: () => {
|
|
450
|
+
state = null;
|
|
451
|
+
authSubscribers.clear();
|
|
163
452
|
},
|
|
164
453
|
get anonymousId() {
|
|
165
|
-
return
|
|
454
|
+
return getState().identity.getAnonymousId();
|
|
166
455
|
},
|
|
167
456
|
get appUserId() {
|
|
168
|
-
return
|
|
457
|
+
return getState().identity.getAppUserId() ?? void 0;
|
|
169
458
|
},
|
|
170
459
|
get isAuthenticated() {
|
|
171
|
-
return
|
|
460
|
+
return getState().identity.isAuthenticated();
|
|
172
461
|
},
|
|
173
462
|
setDebug: (enabled) => {
|
|
174
|
-
|
|
463
|
+
getState().debug = enabled;
|
|
175
464
|
},
|
|
176
465
|
events: { track: async (event, properties) => {
|
|
177
|
-
|
|
466
|
+
const s = getState();
|
|
467
|
+
await s.http.post(P.events, {
|
|
468
|
+
name: event,
|
|
469
|
+
properties: properties ?? void 0,
|
|
470
|
+
anonymous_id: s.identity.getAnonymousId(),
|
|
471
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
472
|
+
});
|
|
178
473
|
} },
|
|
179
474
|
auth: {
|
|
180
475
|
signInAnonymously: async () => {
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
return r;
|
|
476
|
+
const s = getState();
|
|
477
|
+
return postAuth(s, P.authAnonymous, { anonymous_id: s.identity.getAnonymousId() });
|
|
184
478
|
},
|
|
185
479
|
signInWithEmail: async (email, password) => {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
return
|
|
480
|
+
requireEmail(email);
|
|
481
|
+
if (password === "") throw new AmbaApiError("VALIDATION_ERROR", "password must not be empty");
|
|
482
|
+
return postAuth(getState(), P.authEmailLogin, {
|
|
483
|
+
email,
|
|
484
|
+
password
|
|
485
|
+
});
|
|
189
486
|
},
|
|
190
487
|
signUpWithEmail: async (email, password) => {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
return
|
|
488
|
+
requireEmail(email);
|
|
489
|
+
requirePassword(password);
|
|
490
|
+
return postAuth(getState(), P.authEmailSignup, {
|
|
491
|
+
email,
|
|
492
|
+
password
|
|
493
|
+
});
|
|
194
494
|
},
|
|
195
495
|
signInWithSocial: async (provider, idToken) => {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
496
|
+
if (idToken.trim() === "") throw new AmbaApiError("VALIDATION_ERROR", "id_token must not be empty");
|
|
497
|
+
return postAuth(getState(), P.authSocial, {
|
|
498
|
+
provider,
|
|
499
|
+
id_token: idToken
|
|
500
|
+
});
|
|
199
501
|
},
|
|
200
502
|
signOut: async (rotateAnonymousId = false) => {
|
|
201
|
-
|
|
503
|
+
const s = getState();
|
|
504
|
+
if (s.identity.getSessionToken()) try {
|
|
505
|
+
await s.http.post(P.authLogout, {});
|
|
506
|
+
} catch (err) {
|
|
507
|
+
if (typeof console !== "undefined") console.warn("[amba] signOut server call failed", err);
|
|
508
|
+
}
|
|
509
|
+
await s.identity.clearSession(rotateAnonymousId);
|
|
202
510
|
notifyAuthSubscribers(null);
|
|
203
511
|
},
|
|
204
512
|
refresh: async () => {
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
513
|
+
const s = getState();
|
|
514
|
+
const refreshToken = s.identity.getRefreshToken();
|
|
515
|
+
if (!refreshToken) throw new AmbaApiError("UNAUTHORIZED", "no refresh token available — call signIn* first");
|
|
516
|
+
return postAuth(s, P.authRefresh, { refresh_token: refreshToken });
|
|
517
|
+
},
|
|
518
|
+
me: async () => {
|
|
519
|
+
const s = getState();
|
|
520
|
+
if (!s.identity.getSessionToken()) throw new AmbaApiError("UNAUTHORIZED", "no active session — call signIn* first");
|
|
521
|
+
return (await s.http.get(P.authMe)).data;
|
|
208
522
|
},
|
|
209
|
-
me: async () => await coreCall((c) => c.me()),
|
|
210
523
|
getSession: async () => {
|
|
211
|
-
|
|
212
|
-
|
|
524
|
+
const s = getState();
|
|
525
|
+
if (!s.identity.isAuthenticated()) return null;
|
|
526
|
+
return snapshotSession((await s.http.get(P.authMe)).data);
|
|
213
527
|
},
|
|
214
528
|
getAnonymousId: async () => {
|
|
215
|
-
return
|
|
529
|
+
return getState().identity.getAnonymousId();
|
|
216
530
|
},
|
|
217
531
|
onAuthStateChange: (cb) => {
|
|
218
532
|
authSubscribers.add(cb);
|
|
@@ -222,11 +536,21 @@ const Amba = {
|
|
|
222
536
|
}
|
|
223
537
|
},
|
|
224
538
|
collections: {
|
|
225
|
-
find: async (name, options = {}) =>
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
539
|
+
find: async (name, options = {}) => {
|
|
540
|
+
return getState().http.post(`${P.collections}/${encodePathSegment(name)}/find`, options);
|
|
541
|
+
},
|
|
542
|
+
findOne: async (name, id) => {
|
|
543
|
+
return (await getState().http.get(`${P.collections}/${encodePathSegment(name)}/${encodePathSegment(id)}`)).data;
|
|
544
|
+
},
|
|
545
|
+
insert: async (name, row) => {
|
|
546
|
+
return (await getState().http.post(`${P.collections}/${encodePathSegment(name)}`, row)).data;
|
|
547
|
+
},
|
|
548
|
+
update: async (name, id, set) => {
|
|
549
|
+
return (await getState().http.patch(`${P.collections}/${encodePathSegment(name)}/${encodePathSegment(id)}`, { set })).data;
|
|
550
|
+
},
|
|
551
|
+
delete: async (name, id) => {
|
|
552
|
+
return await getState().http.delete(`${P.collections}/${encodePathSegment(name)}/${encodePathSegment(id)}`) ?? { data: { deleted: true } };
|
|
553
|
+
},
|
|
230
554
|
where: {
|
|
231
555
|
eq: (column, value) => ({
|
|
232
556
|
column,
|
|
@@ -292,141 +616,347 @@ const Amba = {
|
|
|
292
616
|
}
|
|
293
617
|
},
|
|
294
618
|
storage: {
|
|
295
|
-
presign: async (params) =>
|
|
296
|
-
|
|
619
|
+
presign: async (params) => {
|
|
620
|
+
return (await getState().http.post(P.storagePresign, {
|
|
621
|
+
bucket: params.bucket,
|
|
622
|
+
filename: params.filename,
|
|
623
|
+
mime_type: params.mimeType,
|
|
624
|
+
size_bytes: params.sizeBytes,
|
|
625
|
+
retention_days: params.retentionDays
|
|
626
|
+
})).data;
|
|
627
|
+
},
|
|
628
|
+
commit: async (uploadId, assetId) => {
|
|
629
|
+
return (await getState().http.post(P.storageCommit, {
|
|
630
|
+
upload_id: uploadId,
|
|
631
|
+
asset_id: assetId
|
|
632
|
+
})).data;
|
|
633
|
+
}
|
|
297
634
|
},
|
|
298
635
|
push: {
|
|
299
|
-
register: async (token, platform, bundleId) =>
|
|
636
|
+
register: async (token, platform, bundleId) => {
|
|
637
|
+
return (await getState().http.post(P.pushRegister, {
|
|
638
|
+
token,
|
|
639
|
+
platform,
|
|
640
|
+
bundle_id: bundleId
|
|
641
|
+
})).data;
|
|
642
|
+
},
|
|
300
643
|
unregister: async (token) => {
|
|
301
|
-
await
|
|
644
|
+
await getState().http.post(P.pushUnregister, { token });
|
|
645
|
+
},
|
|
646
|
+
getTokens: async () => {
|
|
647
|
+
return (await getState().http.get(P.pushTokens)).data;
|
|
302
648
|
},
|
|
303
|
-
getTokens: async () => await coreCall((c) => c.pushGetTokens()),
|
|
304
649
|
subscribe: async (topic) => {
|
|
305
|
-
await
|
|
650
|
+
await getState().http.post(P.pushSubscribe, { topic });
|
|
306
651
|
},
|
|
307
652
|
unsubscribe: async (topic) => {
|
|
308
|
-
await
|
|
653
|
+
await getState().http.post(P.pushUnsubscribe, { topic });
|
|
309
654
|
}
|
|
310
655
|
},
|
|
311
656
|
entitlements: {
|
|
312
|
-
list: async () =>
|
|
313
|
-
|
|
657
|
+
list: async () => {
|
|
658
|
+
return (await getState().http.get(P.entitlements)).data;
|
|
659
|
+
},
|
|
660
|
+
has: async (name) => {
|
|
661
|
+
return (await getState().http.get(P.entitlements, { active_only: "true" })).data.some((e) => e.name === name && e.is_active);
|
|
662
|
+
}
|
|
314
663
|
},
|
|
315
|
-
ai: { anthropic: { messages: { create: async (request) =>
|
|
316
|
-
|
|
317
|
-
|
|
664
|
+
ai: { anthropic: { messages: { create: async (request) => {
|
|
665
|
+
return (await getState().http.post(P.aiAnthropic, request)).data;
|
|
666
|
+
} } } },
|
|
667
|
+
config: { fetch: async () => {
|
|
668
|
+
const s = getState();
|
|
669
|
+
const url = s.http.url(P.config);
|
|
670
|
+
const headers = s.http.buildHeaders();
|
|
671
|
+
let response;
|
|
672
|
+
try {
|
|
673
|
+
response = await (globalThis.fetch ?? fetch)(url, {
|
|
674
|
+
method: "GET",
|
|
675
|
+
headers
|
|
676
|
+
});
|
|
677
|
+
} catch (err) {
|
|
678
|
+
throw new AmbaApiError("NETWORK_ERROR", `Network error: ${err instanceof Error ? err.message : String(err)}`, err);
|
|
679
|
+
}
|
|
680
|
+
const text = await response.text();
|
|
681
|
+
if (!response.ok) {
|
|
682
|
+
const ct = response.headers.get("content-type") ?? "";
|
|
683
|
+
throw mapHttpError(response, text, ct.includes("application/json"));
|
|
684
|
+
}
|
|
685
|
+
let parsed = {};
|
|
686
|
+
if (text) try {
|
|
687
|
+
parsed = JSON.parse(text);
|
|
688
|
+
} catch {
|
|
689
|
+
parsed = {};
|
|
690
|
+
}
|
|
691
|
+
const etag = response.headers.get("etag");
|
|
692
|
+
let version = null;
|
|
693
|
+
if (etag) {
|
|
694
|
+
const cleaned = etag.replace(/^"|"$/g, "");
|
|
695
|
+
const m = cleaned.match(/^(v\d+)/);
|
|
696
|
+
version = m ? m[1] ?? null : cleaned || null;
|
|
697
|
+
}
|
|
698
|
+
return {
|
|
699
|
+
version,
|
|
700
|
+
values: parsed.data ?? {}
|
|
701
|
+
};
|
|
702
|
+
} },
|
|
703
|
+
flags: { fetch: async () => {
|
|
704
|
+
return (await getState().http.get(P.flags)).data;
|
|
705
|
+
} },
|
|
318
706
|
achievements: {
|
|
319
|
-
getAll: async () =>
|
|
320
|
-
|
|
707
|
+
getAll: async () => {
|
|
708
|
+
return (await getState().http.get(P.achievements)).data;
|
|
709
|
+
},
|
|
710
|
+
getProgress: async () => {
|
|
711
|
+
return (await getState().http.get(`${P.achievements}/progress`)).data;
|
|
712
|
+
}
|
|
321
713
|
},
|
|
322
714
|
challenges: {
|
|
323
|
-
getActive: async () =>
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
715
|
+
getActive: async () => {
|
|
716
|
+
return (await getState().http.get(`${P.challenges}/active`)).data;
|
|
717
|
+
},
|
|
718
|
+
get: async (id) => {
|
|
719
|
+
return (await getState().http.get(`${P.challenges}/${encodePathSegment(id)}`)).data;
|
|
720
|
+
},
|
|
721
|
+
getProgress: async (id) => {
|
|
722
|
+
return (await getState().http.get(`${P.challenges}/${encodePathSegment(id)}/progress`)).data;
|
|
723
|
+
},
|
|
724
|
+
claim: async (id) => {
|
|
725
|
+
return (await getState().http.post(`${P.challenges}/${encodePathSegment(id)}/claim`, {})).data;
|
|
726
|
+
}
|
|
327
727
|
},
|
|
328
728
|
currencies: {
|
|
329
|
-
getBalance: async () =>
|
|
330
|
-
|
|
729
|
+
getBalance: async () => {
|
|
730
|
+
return (await getState().http.get(P.currencies)).data;
|
|
731
|
+
},
|
|
732
|
+
getTransactions: async (currencyKey) => {
|
|
733
|
+
return (await getState().http.get(`${P.currencies}/${encodePathSegment(currencyKey)}/transactions`)).data;
|
|
734
|
+
}
|
|
331
735
|
},
|
|
332
736
|
inventory: {
|
|
333
|
-
getItems: async () =>
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
737
|
+
getItems: async () => {
|
|
738
|
+
return (await getState().http.get(P.inventory)).data;
|
|
739
|
+
},
|
|
740
|
+
getItem: async (id) => {
|
|
741
|
+
return (await getState().http.get(`${P.inventory}/${encodePathSegment(id)}`)).data;
|
|
742
|
+
},
|
|
743
|
+
purchase: async (request) => {
|
|
744
|
+
return (await getState().http.post(`${P.inventory}/purchase`, request)).data;
|
|
745
|
+
},
|
|
746
|
+
consume: async (request) => {
|
|
747
|
+
return (await getState().http.post(`${P.inventory}/consume`, request)).data;
|
|
748
|
+
}
|
|
337
749
|
},
|
|
338
750
|
leaderboards: {
|
|
339
|
-
get: async (key) =>
|
|
340
|
-
|
|
341
|
-
|
|
751
|
+
get: async (key) => {
|
|
752
|
+
return (await getState().http.get(`${P.leaderboards}/${encodePathSegment(key)}`)).data;
|
|
753
|
+
},
|
|
754
|
+
getEntries: async (key, limit) => {
|
|
755
|
+
return (await getState().http.get(`${P.leaderboards}/${encodePathSegment(key)}/entries`, limit !== void 0 ? { limit } : void 0)).data;
|
|
756
|
+
},
|
|
757
|
+
getMyRank: async (key) => {
|
|
758
|
+
return (await getState().http.get(`${P.leaderboards}/${encodePathSegment(key)}/me`)).data;
|
|
759
|
+
}
|
|
342
760
|
},
|
|
343
761
|
stores: {
|
|
344
|
-
list: async () =>
|
|
345
|
-
|
|
346
|
-
|
|
762
|
+
list: async () => {
|
|
763
|
+
return (await getState().http.get(P.stores)).data;
|
|
764
|
+
},
|
|
765
|
+
getPurchaseOptions: async (storeKey) => {
|
|
766
|
+
return (await getState().http.get(`${P.stores}/${encodePathSegment(storeKey)}/options`)).data;
|
|
767
|
+
},
|
|
768
|
+
purchase: async (storeKey, purchaseOptionId, receipt) => {
|
|
769
|
+
return (await getState().http.post(`${P.stores}/${encodePathSegment(storeKey)}/purchase`, {
|
|
770
|
+
purchase_option_id: purchaseOptionId,
|
|
771
|
+
receipt
|
|
772
|
+
})).data;
|
|
773
|
+
}
|
|
347
774
|
},
|
|
348
775
|
xp: {
|
|
349
|
-
getBalance: async () =>
|
|
350
|
-
|
|
351
|
-
|
|
776
|
+
getBalance: async () => {
|
|
777
|
+
return (await getState().http.get(P.xp)).data;
|
|
778
|
+
},
|
|
779
|
+
getHistory: async (limit) => {
|
|
780
|
+
return (await getState().http.get(`${P.xp}/history`, limit !== void 0 ? { limit } : void 0)).data;
|
|
781
|
+
},
|
|
782
|
+
claim: async (grantKey) => {
|
|
783
|
+
return (await getState().http.post(`${P.xp}/claim`, { grant_key: grantKey })).data;
|
|
784
|
+
}
|
|
352
785
|
},
|
|
353
786
|
streaks: {
|
|
354
|
-
getAll: async () =>
|
|
355
|
-
|
|
787
|
+
getAll: async () => {
|
|
788
|
+
return (await getState().http.get(P.streaks)).data;
|
|
789
|
+
},
|
|
790
|
+
qualify: async (streakKey) => {
|
|
791
|
+
return (await getState().http.post(`${P.streaks}/${encodePathSegment(streakKey)}/qualify`, {})).data;
|
|
792
|
+
}
|
|
356
793
|
},
|
|
357
|
-
feeds: { getActivity: async (feed, cursor) =>
|
|
794
|
+
feeds: { getActivity: async (feed, cursor) => {
|
|
795
|
+
const f = feed ?? "default";
|
|
796
|
+
return getState().http.get(`${P.feeds}/${encodePathSegment(f)}`, cursor ? { cursor } : void 0);
|
|
797
|
+
} },
|
|
358
798
|
friends: {
|
|
359
|
-
getList: async () =>
|
|
360
|
-
|
|
361
|
-
|
|
799
|
+
getList: async () => {
|
|
800
|
+
return (await getState().http.get(P.friends)).data;
|
|
801
|
+
},
|
|
802
|
+
getFriends: async () => {
|
|
803
|
+
return (await getState().http.get(P.friends, { state: "accepted" })).data;
|
|
804
|
+
},
|
|
805
|
+
blockUser: async (userId) => {
|
|
806
|
+
return (await getState().http.post(`${P.friends}/block`, { user_id: userId })).data;
|
|
807
|
+
},
|
|
362
808
|
unblockUser: async (userId) => {
|
|
363
|
-
await
|
|
809
|
+
await getState().http.post(`${P.friends}/unblock`, { user_id: userId });
|
|
364
810
|
},
|
|
365
811
|
removeBlock: async (friendshipId) => {
|
|
366
|
-
await
|
|
812
|
+
await getState().http.delete(`${P.friends}/${encodePathSegment(friendshipId)}`);
|
|
367
813
|
}
|
|
368
814
|
},
|
|
369
815
|
groups: {
|
|
370
|
-
create: async (params) =>
|
|
371
|
-
|
|
372
|
-
|
|
816
|
+
create: async (params) => {
|
|
817
|
+
return (await getState().http.post(P.groups, params)).data;
|
|
818
|
+
},
|
|
819
|
+
get: async (id) => {
|
|
820
|
+
return (await getState().http.get(`${P.groups}/${encodePathSegment(id)}`)).data;
|
|
821
|
+
},
|
|
822
|
+
update: async (id, patch) => {
|
|
823
|
+
return (await getState().http.patch(`${P.groups}/${encodePathSegment(id)}`, patch)).data;
|
|
824
|
+
},
|
|
373
825
|
delete: async (id) => {
|
|
374
|
-
await
|
|
826
|
+
await getState().http.delete(`${P.groups}/${encodePathSegment(id)}`);
|
|
827
|
+
},
|
|
828
|
+
getMembers: async (id) => {
|
|
829
|
+
return (await getState().http.get(`${P.groups}/${encodePathSegment(id)}/members`)).data;
|
|
830
|
+
},
|
|
831
|
+
join: async (id) => {
|
|
832
|
+
return (await getState().http.post(`${P.groups}/${encodePathSegment(id)}/join`, {})).data;
|
|
375
833
|
},
|
|
376
|
-
getMembers: async (id) => await coreCall((c) => c.groupsGetMembers(id)),
|
|
377
|
-
join: async (id) => await coreCall((c) => c.groupsJoin(id)),
|
|
378
834
|
leave: async (id) => {
|
|
379
|
-
await
|
|
835
|
+
await getState().http.post(`${P.groups}/${encodePathSegment(id)}/leave`, {});
|
|
380
836
|
},
|
|
381
|
-
invite: async (id, userId) =>
|
|
837
|
+
invite: async (id, userId) => {
|
|
838
|
+
return (await getState().http.post(`${P.groups}/${encodePathSegment(id)}/invite`, { user_id: userId })).data;
|
|
839
|
+
}
|
|
382
840
|
},
|
|
383
841
|
messaging: {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
842
|
+
createConversation: async (request) => {
|
|
843
|
+
return (await getState().http.post(`${P.messaging}/conversations`, request)).data;
|
|
844
|
+
},
|
|
845
|
+
getConversations: async () => {
|
|
846
|
+
return (await getState().http.get(`${P.messaging}/conversations`)).data;
|
|
847
|
+
},
|
|
848
|
+
listConversations: async () => {
|
|
849
|
+
return (await getState().http.get(`${P.messaging}/conversations`)).data;
|
|
850
|
+
},
|
|
851
|
+
sendMessage: async (conversationId, request) => {
|
|
852
|
+
return (await getState().http.post(`${P.messaging}/conversations/${encodePathSegment(conversationId)}/messages`, request)).data;
|
|
853
|
+
},
|
|
854
|
+
listMessages: async (conversationId, opts) => {
|
|
855
|
+
const query = {};
|
|
856
|
+
if (opts?.limit !== void 0) query.limit = opts.limit;
|
|
857
|
+
if (opts?.offset !== void 0) query.offset = opts.offset;
|
|
858
|
+
return (await getState().http.get(`${P.messaging}/conversations/${encodePathSegment(conversationId)}/messages`, Object.keys(query).length > 0 ? query : void 0)).data;
|
|
859
|
+
},
|
|
860
|
+
markRead: async (conversationId) => {
|
|
861
|
+
return (await getState().http.post(`${P.messaging}/conversations/${encodePathSegment(conversationId)}/read`, {})).data;
|
|
862
|
+
}
|
|
387
863
|
},
|
|
388
864
|
moderation: {
|
|
389
|
-
reportUser: async (request) =>
|
|
390
|
-
|
|
391
|
-
|
|
865
|
+
reportUser: async (request) => {
|
|
866
|
+
return (await getState().http.post(`${P.moderation}/reports/users`, request)).data;
|
|
867
|
+
},
|
|
868
|
+
reportContent: async (request) => {
|
|
869
|
+
return (await getState().http.post(`${P.moderation}/reports/content`, request)).data;
|
|
870
|
+
},
|
|
871
|
+
getReportStatus: async (id) => {
|
|
872
|
+
return (await getState().http.get(`${P.moderation}/reports/${encodePathSegment(id)}`)).data;
|
|
873
|
+
}
|
|
392
874
|
},
|
|
393
875
|
reviews: {
|
|
394
|
-
list: async (targetType, targetId) =>
|
|
395
|
-
|
|
396
|
-
|
|
876
|
+
list: async (targetType, targetId) => {
|
|
877
|
+
return (await getState().http.get(P.reviews, {
|
|
878
|
+
target_type: targetType,
|
|
879
|
+
target_id: targetId
|
|
880
|
+
})).data;
|
|
881
|
+
},
|
|
882
|
+
create: async (params) => {
|
|
883
|
+
return (await getState().http.post(P.reviews, params)).data;
|
|
884
|
+
},
|
|
885
|
+
update: async (id, patch) => {
|
|
886
|
+
return (await getState().http.patch(`${P.reviews}/${encodePathSegment(id)}`, patch)).data;
|
|
887
|
+
},
|
|
397
888
|
delete: async (id) => {
|
|
398
|
-
await
|
|
889
|
+
await getState().http.delete(`${P.reviews}/${encodePathSegment(id)}`);
|
|
399
890
|
}
|
|
400
891
|
},
|
|
401
892
|
roles: {
|
|
402
|
-
getMyRoles: async () =>
|
|
403
|
-
|
|
893
|
+
getMyRoles: async () => {
|
|
894
|
+
return (await getState().http.get(`${P.roles}/me`)).data;
|
|
895
|
+
},
|
|
896
|
+
hasPermission: async (permission) => {
|
|
897
|
+
return (await getState().http.get(`${P.roles}/check`, { permission })).data.allowed === true;
|
|
898
|
+
}
|
|
404
899
|
},
|
|
405
900
|
referrals: {
|
|
406
|
-
getReferralCode: async () =>
|
|
407
|
-
|
|
408
|
-
|
|
901
|
+
getReferralCode: async () => {
|
|
902
|
+
return (await getState().http.get(`${P.referrals}/me`)).data;
|
|
903
|
+
},
|
|
904
|
+
claimReferral: async (code) => {
|
|
905
|
+
return (await getState().http.post(`${P.referrals}/claim`, { code })).data;
|
|
906
|
+
},
|
|
907
|
+
create: async (code, maxUses) => {
|
|
908
|
+
const body = {};
|
|
909
|
+
if (code !== void 0) body.code = code;
|
|
910
|
+
if (maxUses !== void 0) body.max_uses = maxUses;
|
|
911
|
+
return (await getState().http.post(P.referrals, body)).data;
|
|
912
|
+
}
|
|
409
913
|
},
|
|
410
|
-
catalog: { list: async () =>
|
|
914
|
+
catalog: { list: async () => {
|
|
915
|
+
return (await getState().http.get(P.catalog)).data;
|
|
916
|
+
} },
|
|
411
917
|
content: {
|
|
412
|
-
getToday: async (channel = "default") =>
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
918
|
+
getToday: async (channel = "default") => {
|
|
919
|
+
return (await getState().http.get(`${P.content}/today/${encodePathSegment(channel)}`)).data ?? null;
|
|
920
|
+
},
|
|
921
|
+
getLibrary: async (channel = "default", options) => {
|
|
922
|
+
return (await getState().http.get(`${P.content}/library/${encodePathSegment(channel)}`, {
|
|
923
|
+
limit: options?.limit,
|
|
924
|
+
cursor: options?.cursor
|
|
925
|
+
})).data;
|
|
926
|
+
},
|
|
927
|
+
getItem: async (id) => {
|
|
928
|
+
return (await getState().http.get(`${P.content}/${encodePathSegment(id)}`)).data;
|
|
929
|
+
},
|
|
930
|
+
updateItem: async (id, state_) => {
|
|
931
|
+
return (await getState().http.post(`${P.content}/${encodePathSegment(id)}/state`, state_)).data;
|
|
932
|
+
},
|
|
933
|
+
createItem: async (channel, item) => {
|
|
934
|
+
return (await getState().http.post(`${P.content}/${encodePathSegment(channel)}`, item)).data;
|
|
935
|
+
}
|
|
417
936
|
},
|
|
418
937
|
deepLinks: {
|
|
419
|
-
get: async (shortCode) =>
|
|
420
|
-
|
|
938
|
+
get: async (shortCode) => {
|
|
939
|
+
return (await getState().http.get(`${P.deepLinks}/${encodePathSegment(shortCode)}`)).data;
|
|
940
|
+
},
|
|
941
|
+
create: async (params) => {
|
|
942
|
+
return (await getState().http.post(P.deepLinks, params)).data;
|
|
943
|
+
}
|
|
421
944
|
},
|
|
422
945
|
onboarding: {
|
|
423
|
-
getStatus: async () =>
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
946
|
+
getStatus: async () => {
|
|
947
|
+
return (await getState().http.get(P.onboarding)).data;
|
|
948
|
+
},
|
|
949
|
+
nextStep: async (payload) => {
|
|
950
|
+
return (await getState().http.post(`${P.onboarding}/next`, payload)).data;
|
|
951
|
+
},
|
|
952
|
+
skipStep: async () => {
|
|
953
|
+
return (await getState().http.post(`${P.onboarding}/skip`, {})).data;
|
|
954
|
+
},
|
|
955
|
+
complete: async () => {
|
|
956
|
+
return (await getState().http.post(`${P.onboarding}/complete`, {})).data;
|
|
957
|
+
}
|
|
427
958
|
}
|
|
428
959
|
};
|
|
429
|
-
const SDK_VERSION = "0.1.0";
|
|
430
960
|
|
|
431
961
|
//#endregion
|
|
432
962
|
export { Amba, AmbaApiError, SDK_VERSION };
|