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