@layers/amba-web 4.0.8 → 4.0.10
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.browser.d.ts +89 -9
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +287 -53
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +89 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +286 -41
- package/dist/index.js.map +1 -1
- package/dist-rn/index.d.ts +89 -9
- package/dist-rn/index.js +286 -41
- package/package.json +2 -2
package/dist/index.browser.js
CHANGED
|
@@ -24,28 +24,46 @@ async function getCoreRuntime() {
|
|
|
24
24
|
/**
|
|
25
25
|
* Browser localStorage adapter for SDK identity persistence.
|
|
26
26
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* (eventual) identity cache.
|
|
27
|
+
* Browsers can deny or throw on localStorage access (privacy mode, sandboxed
|
|
28
|
+
* iframes, quota exhaustion). Persistence is best-effort: storage failures
|
|
29
|
+
* must never turn a successful auth/network operation into an SDK failure.
|
|
31
30
|
*/
|
|
32
31
|
var LocalStoragePersistence = class {
|
|
33
32
|
constructor(prefix) {
|
|
34
33
|
this.prefix = prefix;
|
|
35
34
|
}
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
get storage() {
|
|
36
|
+
try {
|
|
37
|
+
return globalThis.localStorage ?? null;
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
get available() {
|
|
43
|
+
return this.storage !== null;
|
|
44
|
+
}
|
|
38
45
|
read(key) {
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
const storage = this.storage;
|
|
47
|
+
if (!storage) return null;
|
|
48
|
+
try {
|
|
49
|
+
return storage.getItem(`${this.prefix}:${key}`);
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
41
53
|
}
|
|
42
54
|
write(key, value) {
|
|
43
|
-
|
|
44
|
-
|
|
55
|
+
const storage = this.storage;
|
|
56
|
+
if (!storage) return;
|
|
57
|
+
try {
|
|
58
|
+
storage.setItem(`${this.prefix}:${key}`, value);
|
|
59
|
+
} catch {}
|
|
45
60
|
}
|
|
46
61
|
delete(key) {
|
|
47
|
-
|
|
48
|
-
|
|
62
|
+
const storage = this.storage;
|
|
63
|
+
if (!storage) return;
|
|
64
|
+
try {
|
|
65
|
+
storage.removeItem(`${this.prefix}:${key}`);
|
|
66
|
+
} catch {}
|
|
49
67
|
}
|
|
50
68
|
};
|
|
51
69
|
|
|
@@ -1342,11 +1360,10 @@ const DEFAULT_API_URL = "https://api.amba.dev";
|
|
|
1342
1360
|
/**
|
|
1343
1361
|
* Default host that serves deployed functions. `Amba.functions.invoke(name)`
|
|
1344
1362
|
* targets `https://<projectId>.<DEFAULT_FUNCTIONS_HOST>/<name>` unless a
|
|
1345
|
-
* `functionsBaseUrl` override is configured.
|
|
1346
|
-
* server-resolved id (from `diagnostics.ping`), which the function router
|
|
1347
|
-
* accepts directly.
|
|
1363
|
+
* `functionsBaseUrl` override is configured.
|
|
1348
1364
|
*/
|
|
1349
1365
|
const DEFAULT_FUNCTIONS_HOST = "fn.amba.host";
|
|
1366
|
+
const PROJECT_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1350
1367
|
/**
|
|
1351
1368
|
* Internal state — one instance per page. Created lazily by `Amba.configure()`.
|
|
1352
1369
|
*/
|
|
@@ -1368,15 +1385,73 @@ let _configuredProjectId = null;
|
|
|
1368
1385
|
let _configuredApiUrl = DEFAULT_API_URL;
|
|
1369
1386
|
let _configuredClientKey = null;
|
|
1370
1387
|
let _currentSessionToken = null;
|
|
1388
|
+
let _currentRefreshToken = null;
|
|
1389
|
+
let _currentSessionExpiresAt = null;
|
|
1390
|
+
let _currentUser = null;
|
|
1391
|
+
const BROWSER_IDENTITY_VERSION = 1;
|
|
1392
|
+
const DEFAULT_SESSION_TTL_MS = 3600 * 1e3;
|
|
1393
|
+
function identityStorageKey(projectId) {
|
|
1394
|
+
return `identity:v${BROWSER_IDENTITY_VERSION}:project:${encodeURIComponent(projectId)}`;
|
|
1395
|
+
}
|
|
1396
|
+
function isNonEmptyString(value) {
|
|
1397
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
1398
|
+
}
|
|
1399
|
+
function containsControlCharacter(value) {
|
|
1400
|
+
return Array.from(value).some((char) => {
|
|
1401
|
+
const code = char.charCodeAt(0);
|
|
1402
|
+
return code <= 31 || code === 127;
|
|
1403
|
+
});
|
|
1404
|
+
}
|
|
1405
|
+
/** Read + validate a project-bound identity snapshot. Corrupt or cross-project
|
|
1406
|
+
* data is removed instead of being handed to the core. */
|
|
1407
|
+
function readPersistedIdentity(persistence, projectId) {
|
|
1408
|
+
const key = identityStorageKey(projectId);
|
|
1409
|
+
const raw = persistence.read(key);
|
|
1410
|
+
if (!raw) return null;
|
|
1411
|
+
try {
|
|
1412
|
+
const value = JSON.parse(raw);
|
|
1413
|
+
const hasTokens = value.session_token != null || value.refresh_token != null;
|
|
1414
|
+
if (value.version !== BROWSER_IDENTITY_VERSION || value.project_id !== projectId || !isNonEmptyString(value.anonymous_id) || hasTokens && !isNonEmptyString(value.app_user_id) || value.session_token != null && !isNonEmptyString(value.refresh_token) || value.session_token != null && !isNonEmptyString(value.session_token) || value.refresh_token != null && !isNonEmptyString(value.refresh_token) || value.expires_at != null && (!isNonEmptyString(value.expires_at) || !Number.isFinite(Date.parse(value.expires_at)))) throw new Error("invalid identity snapshot");
|
|
1415
|
+
const expiresAt = isNonEmptyString(value.expires_at) ? Date.parse(value.expires_at) : NaN;
|
|
1416
|
+
const sessionExpired = value.session_token != null && Number.isFinite(expiresAt) && expiresAt <= Date.now();
|
|
1417
|
+
return {
|
|
1418
|
+
version: BROWSER_IDENTITY_VERSION,
|
|
1419
|
+
project_id: projectId,
|
|
1420
|
+
anonymous_id: value.anonymous_id,
|
|
1421
|
+
app_user_id: isNonEmptyString(value.app_user_id) ? value.app_user_id : null,
|
|
1422
|
+
session_token: sessionExpired ? null : value.session_token ?? null,
|
|
1423
|
+
refresh_token: value.refresh_token ?? null,
|
|
1424
|
+
expires_at: sessionExpired ? null : value.expires_at ?? null,
|
|
1425
|
+
user: value.user && typeof value.user === "object" && isNonEmptyString(value.user.id) ? value.user : null
|
|
1426
|
+
};
|
|
1427
|
+
} catch {
|
|
1428
|
+
persistence.delete(key);
|
|
1429
|
+
return null;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
function writeIdentitySnapshot() {
|
|
1433
|
+
if (!_persistence?.available || !_configuredProjectId || !coreInstance) return;
|
|
1434
|
+
const snapshot = {
|
|
1435
|
+
version: BROWSER_IDENTITY_VERSION,
|
|
1436
|
+
project_id: _configuredProjectId,
|
|
1437
|
+
anonymous_id: coreInstance.anonymousId(),
|
|
1438
|
+
app_user_id: coreInstance.appUserId() ?? null,
|
|
1439
|
+
session_token: _currentSessionToken,
|
|
1440
|
+
refresh_token: _currentRefreshToken,
|
|
1441
|
+
expires_at: _currentSessionExpiresAt,
|
|
1442
|
+
user: _currentUser
|
|
1443
|
+
};
|
|
1444
|
+
_persistence.write(identityStorageKey(_configuredProjectId), JSON.stringify(snapshot));
|
|
1445
|
+
}
|
|
1371
1446
|
/**
|
|
1372
1447
|
* Functions invocation state. `_configuredFunctionsBaseUrl` is the optional
|
|
1373
|
-
* override from `Amba.configure({ functionsBaseUrl })`.
|
|
1374
|
-
*
|
|
1375
|
-
*
|
|
1376
|
-
* round-trip. Both reset on every `configure()`.
|
|
1448
|
+
* override from `Amba.configure({ functionsBaseUrl })`. The default-host path
|
|
1449
|
+
* caches its resolved project id and shares an in-flight diagnostics fallback
|
|
1450
|
+
* across concurrent invokes. All three reset on every `configure()`.
|
|
1377
1451
|
*/
|
|
1378
1452
|
let _configuredFunctionsBaseUrl = null;
|
|
1379
1453
|
let _resolvedFunctionsProjectId = null;
|
|
1454
|
+
let _functionsProjectIdInflight = null;
|
|
1380
1455
|
/**
|
|
1381
1456
|
* Offline-resilience state. `_persistence` is the localStorage-backed store
|
|
1382
1457
|
* captured at configure-time; `_offlineQueue` is lazily built the first time
|
|
@@ -1444,7 +1519,7 @@ async function replayOutboxOp(op) {
|
|
|
1444
1519
|
switch (op.kind) {
|
|
1445
1520
|
case "collection.insert":
|
|
1446
1521
|
await coreCall((c) => c.collectionsInsert(op.collection, encodeJson({
|
|
1447
|
-
...op.payload
|
|
1522
|
+
...op.payload,
|
|
1448
1523
|
idempotency_key: op.idempotencyKey
|
|
1449
1524
|
})));
|
|
1450
1525
|
return;
|
|
@@ -1565,12 +1640,12 @@ function toWireTransactionOp(op) {
|
|
|
1565
1640
|
return wire;
|
|
1566
1641
|
}
|
|
1567
1642
|
function snapshotSession(user) {
|
|
1568
|
-
if (!user) return null;
|
|
1643
|
+
if (!user || !_currentSessionToken) return null;
|
|
1569
1644
|
return {
|
|
1570
|
-
sessionToken:
|
|
1571
|
-
refreshToken: "",
|
|
1645
|
+
sessionToken: _currentSessionToken,
|
|
1646
|
+
refreshToken: _currentRefreshToken ?? "",
|
|
1572
1647
|
user,
|
|
1573
|
-
expiresAt: ""
|
|
1648
|
+
expiresAt: _currentSessionExpiresAt ?? ""
|
|
1574
1649
|
};
|
|
1575
1650
|
}
|
|
1576
1651
|
function notifyAuthSubscribers(session) {
|
|
@@ -1581,7 +1656,11 @@ function notifyAuthSubscribers(session) {
|
|
|
1581
1656
|
}
|
|
1582
1657
|
}
|
|
1583
1658
|
async function notifyFromAuthResult(result) {
|
|
1584
|
-
|
|
1659
|
+
_currentSessionToken = result.session_token;
|
|
1660
|
+
_currentRefreshToken = result.refresh_token;
|
|
1661
|
+
_currentSessionExpiresAt = result.expires_at && Number.isFinite(Date.parse(result.expires_at)) ? result.expires_at : new Date(Date.now() + DEFAULT_SESSION_TTL_MS).toISOString();
|
|
1662
|
+
_currentUser = result.user;
|
|
1663
|
+
writeIdentitySnapshot();
|
|
1585
1664
|
notifyAuthSubscribers(snapshotSession(result.user));
|
|
1586
1665
|
}
|
|
1587
1666
|
/**
|
|
@@ -1628,22 +1707,156 @@ function buildAiStreamRequest(path, request) {
|
|
|
1628
1707
|
/**
|
|
1629
1708
|
* Resolve the full invocation URL for a deployed function.
|
|
1630
1709
|
*
|
|
1631
|
-
*
|
|
1632
|
-
*
|
|
1633
|
-
*
|
|
1634
|
-
*
|
|
1635
|
-
* project id as the host label directly.
|
|
1710
|
+
* Resolution checks `functionsBaseUrl` first, then uses a UUID-shaped
|
|
1711
|
+
* configured project id directly, and finally falls back to a deduped
|
|
1712
|
+
* `diagnostics.ping` for the authoritative server-side project id. The
|
|
1713
|
+
* resolved default-host id is cached for subsequent invokes.
|
|
1636
1714
|
*/
|
|
1637
1715
|
async function resolveFunctionUrl(name) {
|
|
1638
1716
|
if (_configuredFunctionsBaseUrl) return `${_configuredFunctionsBaseUrl.replace(/\/$/, "")}/${encodeURIComponent(name)}`;
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1717
|
+
return `https://${await resolveFunctionsProjectId()}.${DEFAULT_FUNCTIONS_HOST}/${encodeURIComponent(name)}`;
|
|
1718
|
+
}
|
|
1719
|
+
async function resolveFunctionsProjectId() {
|
|
1720
|
+
if (_resolvedFunctionsProjectId) return _resolvedFunctionsProjectId;
|
|
1721
|
+
if (_configuredProjectId && PROJECT_ID_PATTERN.test(_configuredProjectId)) {
|
|
1722
|
+
_resolvedFunctionsProjectId = _configuredProjectId.toLowerCase();
|
|
1723
|
+
return _resolvedFunctionsProjectId;
|
|
1643
1724
|
}
|
|
1644
|
-
|
|
1725
|
+
if (!_functionsProjectIdInflight) {
|
|
1726
|
+
const configuredProjectId = _configuredProjectId;
|
|
1727
|
+
const resolution = (async () => {
|
|
1728
|
+
const ping = await Amba.diagnostics.ping();
|
|
1729
|
+
if (!ping.ok || !ping.server_project_id) throw new AmbaApiError("FUNCTIONS_HOST_UNRESOLVED", "Could not resolve the functions host for this project. Pass `functionsBaseUrl` to Amba.configure() to invoke functions explicitly.", ping);
|
|
1730
|
+
if (_configuredProjectId === configuredProjectId && !_configuredFunctionsBaseUrl) _resolvedFunctionsProjectId = ping.server_project_id;
|
|
1731
|
+
return ping.server_project_id;
|
|
1732
|
+
})();
|
|
1733
|
+
_functionsProjectIdInflight = resolution;
|
|
1734
|
+
resolution.then(() => {
|
|
1735
|
+
if (_functionsProjectIdInflight === resolution) _functionsProjectIdInflight = null;
|
|
1736
|
+
}, () => {
|
|
1737
|
+
if (_functionsProjectIdInflight === resolution) _functionsProjectIdInflight = null;
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1740
|
+
return _functionsProjectIdInflight;
|
|
1645
1741
|
}
|
|
1646
1742
|
/**
|
|
1743
|
+
* Immutable browser collection scope for a server-verified group context.
|
|
1744
|
+
* Each method carries its own `groupId` into the Rust request path; no mutable
|
|
1745
|
+
* global header state is used, so parallel group A/group B calls cannot leak.
|
|
1746
|
+
*
|
|
1747
|
+
* Group-scoped writes deliberately bypass the global offline outbox. Replaying
|
|
1748
|
+
* a queued group mutation without its membership context would be unsafe;
|
|
1749
|
+
* callers receive the real network outcome and can retry in the same scope.
|
|
1750
|
+
*/
|
|
1751
|
+
var AmbaGroupScopedClient = class AmbaGroupScopedClient {
|
|
1752
|
+
groupId;
|
|
1753
|
+
constructor(groupId) {
|
|
1754
|
+
const normalized = groupId.trim();
|
|
1755
|
+
if (!normalized || containsControlCharacter(normalized)) throw new AmbaApiError("VALIDATION_ERROR", "Amba.asGroup: groupId must be non-empty and contain no control characters");
|
|
1756
|
+
this.groupId = normalized;
|
|
1757
|
+
}
|
|
1758
|
+
asGroup(groupId) {
|
|
1759
|
+
return new AmbaGroupScopedClient(groupId);
|
|
1760
|
+
}
|
|
1761
|
+
inGroup(groupId) {
|
|
1762
|
+
return this.asGroup(groupId);
|
|
1763
|
+
}
|
|
1764
|
+
collection(name) {
|
|
1765
|
+
const groupId = this.groupId;
|
|
1766
|
+
return new CollectionSubscription({
|
|
1767
|
+
baseUrl: _configuredApiUrl,
|
|
1768
|
+
authHeaders: () => {
|
|
1769
|
+
const headers = realtimeAuthHeaders();
|
|
1770
|
+
return headers ? {
|
|
1771
|
+
...headers,
|
|
1772
|
+
"X-Group-Context": groupId
|
|
1773
|
+
} : null;
|
|
1774
|
+
}
|
|
1775
|
+
}, name);
|
|
1776
|
+
}
|
|
1777
|
+
collections = {
|
|
1778
|
+
find: async (name, options = {}) => await coreCall((c) => c.collectionsFind(name, encodeJson(options), this.groupId)),
|
|
1779
|
+
findOne: async (name, id) => await coreCall((c) => c.collectionsFindOne(name, id, this.groupId)),
|
|
1780
|
+
insert: async (name, row) => await coreCall((c) => c.collectionsInsert(name, encodeJson(row), this.groupId)),
|
|
1781
|
+
update: async (name, id, set) => await coreCall((c) => c.collectionsUpdate(name, id, encodeJson(set), this.groupId)),
|
|
1782
|
+
delete: async (name, id) => await coreCall((c) => c.collectionsDelete(name, id, this.groupId)),
|
|
1783
|
+
upsert: async (name, row, options) => await coreCall((c) => c.collectionsUpsert(name, encodeJson(row), encodeJson({
|
|
1784
|
+
on_conflict: options.onConflict,
|
|
1785
|
+
conflict_target: options.conflictTarget
|
|
1786
|
+
}), this.groupId)),
|
|
1787
|
+
deleteWhere: async (name, filter) => await coreCall((c) => c.collectionsDeleteWhere(name, encodeJson(filter), this.groupId)),
|
|
1788
|
+
restore: async (name, id) => await coreCall((c) => c.collectionsRestore(name, id, this.groupId)),
|
|
1789
|
+
transaction: async (operations) => {
|
|
1790
|
+
return (await coreCall((c) => c.collectionsTransaction(encodeJson(operations.map(toWireTransactionOp)), this.groupId))).data;
|
|
1791
|
+
},
|
|
1792
|
+
findNearest: async (name, options) => await coreCall((c) => c.collectionsFindNearest(name, encodeJson(options), this.groupId)),
|
|
1793
|
+
count: async (name, filter) => await coreCall((c) => c.collectionsCount(name, filter ? encodeJson(filter) : void 0, this.groupId)),
|
|
1794
|
+
optimisticInsert: async (name, row, view) => withOptimistic({
|
|
1795
|
+
apply: view.apply,
|
|
1796
|
+
mutate: () => coreCall((c) => c.collectionsInsert(name, encodeJson(row), this.groupId)),
|
|
1797
|
+
rollback: view.rollback
|
|
1798
|
+
}),
|
|
1799
|
+
optimisticUpdate: async (name, id, set, view) => withOptimistic({
|
|
1800
|
+
apply: view.apply,
|
|
1801
|
+
mutate: () => coreCall((c) => c.collectionsUpdate(name, id, encodeJson(set), this.groupId)),
|
|
1802
|
+
rollback: view.rollback
|
|
1803
|
+
}),
|
|
1804
|
+
optimisticDelete: async (name, id, view) => withOptimistic({
|
|
1805
|
+
apply: view.apply,
|
|
1806
|
+
mutate: () => coreCall((c) => c.collectionsDelete(name, id, this.groupId)),
|
|
1807
|
+
rollback: view.rollback
|
|
1808
|
+
}),
|
|
1809
|
+
where: {
|
|
1810
|
+
eq: (column, value) => ({
|
|
1811
|
+
column,
|
|
1812
|
+
op: "eq",
|
|
1813
|
+
value
|
|
1814
|
+
}),
|
|
1815
|
+
ne: (column, value) => ({
|
|
1816
|
+
column,
|
|
1817
|
+
op: "ne",
|
|
1818
|
+
value
|
|
1819
|
+
}),
|
|
1820
|
+
gt: (column, value) => ({
|
|
1821
|
+
column,
|
|
1822
|
+
op: "gt",
|
|
1823
|
+
value
|
|
1824
|
+
}),
|
|
1825
|
+
gte: (column, value) => ({
|
|
1826
|
+
column,
|
|
1827
|
+
op: "gte",
|
|
1828
|
+
value
|
|
1829
|
+
}),
|
|
1830
|
+
lt: (column, value) => ({
|
|
1831
|
+
column,
|
|
1832
|
+
op: "lt",
|
|
1833
|
+
value
|
|
1834
|
+
}),
|
|
1835
|
+
lte: (column, value) => ({
|
|
1836
|
+
column,
|
|
1837
|
+
op: "lte",
|
|
1838
|
+
value
|
|
1839
|
+
}),
|
|
1840
|
+
in: (column, values) => ({
|
|
1841
|
+
column,
|
|
1842
|
+
op: "in",
|
|
1843
|
+
value: values
|
|
1844
|
+
}),
|
|
1845
|
+
isNull: (column) => ({
|
|
1846
|
+
column,
|
|
1847
|
+
op: "is_null"
|
|
1848
|
+
}),
|
|
1849
|
+
isNotNull: (column) => ({
|
|
1850
|
+
column,
|
|
1851
|
+
op: "is_not_null"
|
|
1852
|
+
}),
|
|
1853
|
+
and: (...filters) => ({ and: filters }),
|
|
1854
|
+
or: (...filters) => ({ or: filters }),
|
|
1855
|
+
not: (filter) => ({ not: filter })
|
|
1856
|
+
}
|
|
1857
|
+
};
|
|
1858
|
+
};
|
|
1859
|
+
/**
|
|
1647
1860
|
* Top-level SDK namespace.
|
|
1648
1861
|
*
|
|
1649
1862
|
* Stateless from the JS side — all state lives in the SDK runtime. Calling
|
|
@@ -1658,21 +1871,10 @@ const Amba = {
|
|
|
1658
1871
|
const { initCore } = await getCoreRuntime();
|
|
1659
1872
|
const explicitWasmInput = config.wasmModule ?? config.wasmUrl;
|
|
1660
1873
|
if (explicitWasmInput !== void 0) await initCore({ module_or_path: explicitWasmInput });
|
|
1661
|
-
else
|
|
1662
|
-
const [{ readFile }, { fileURLToPath, pathToFileURL }, { createRequire }] = await Promise.all([
|
|
1663
|
-
import("node:fs/promises"),
|
|
1664
|
-
import("node:url"),
|
|
1665
|
-
import("node:module")
|
|
1666
|
-
]);
|
|
1667
|
-
const resolveFn = import.meta.resolve;
|
|
1668
|
-
let wasmJsUrl;
|
|
1669
|
-
if (resolveFn) wasmJsUrl = await resolveFn("@layers/amba-core/wasm");
|
|
1670
|
-
else wasmJsUrl = pathToFileURL(createRequire(import.meta.url).resolve("@layers/amba-core/wasm")).href;
|
|
1671
|
-
await initCore({ module_or_path: await readFile(fileURLToPath(wasmJsUrl)) });
|
|
1672
|
-
} else await initCore();
|
|
1874
|
+
else await initCore();
|
|
1673
1875
|
}
|
|
1674
1876
|
const persistence = new LocalStoragePersistence(config.storagePrefix ?? "amba");
|
|
1675
|
-
persistence.
|
|
1877
|
+
const restoredIdentity = readPersistedIdentity(persistence, config.projectId);
|
|
1676
1878
|
let nextCore;
|
|
1677
1879
|
try {
|
|
1678
1880
|
const { wasmClass } = await getCoreRuntime();
|
|
@@ -1685,6 +1887,13 @@ const Amba = {
|
|
|
1685
1887
|
consent_required: config.consentRequired,
|
|
1686
1888
|
debug: config.debug
|
|
1687
1889
|
}), globalThis.fetch.bind(globalThis));
|
|
1890
|
+
if (restoredIdentity) nextCore.hydrateIdentity(encodeJson({
|
|
1891
|
+
anonymous_id: restoredIdentity.anonymous_id,
|
|
1892
|
+
app_user_id: restoredIdentity.app_user_id,
|
|
1893
|
+
session_token: restoredIdentity.session_token,
|
|
1894
|
+
refresh_token: restoredIdentity.refresh_token,
|
|
1895
|
+
expires_at: restoredIdentity.expires_at
|
|
1896
|
+
}));
|
|
1688
1897
|
} catch (err) {
|
|
1689
1898
|
throw toAmbaApiError(err);
|
|
1690
1899
|
}
|
|
@@ -1694,8 +1903,15 @@ const Amba = {
|
|
|
1694
1903
|
_configuredProjectId = config.projectId;
|
|
1695
1904
|
_configuredApiUrl = config.apiUrl ?? DEFAULT_API_URL;
|
|
1696
1905
|
_configuredClientKey = config.clientKey;
|
|
1906
|
+
_currentSessionToken = nextCore.isAuthenticated() ? restoredIdentity?.session_token ?? null : null;
|
|
1907
|
+
_currentRefreshToken = restoredIdentity?.refresh_token ?? null;
|
|
1908
|
+
_currentSessionExpiresAt = nextCore.isAuthenticated() ? restoredIdentity?.expires_at ?? null : null;
|
|
1909
|
+
_currentUser = nextCore.isAuthenticated() ? restoredIdentity?.user ?? null : null;
|
|
1697
1910
|
_configuredFunctionsBaseUrl = config.functionsBaseUrl ?? null;
|
|
1698
1911
|
_resolvedFunctionsProjectId = null;
|
|
1912
|
+
_functionsProjectIdInflight = null;
|
|
1913
|
+
if (!_configuredFunctionsBaseUrl && !PROJECT_ID_PATTERN.test(config.projectId)) resolveFunctionsProjectId().catch(() => {});
|
|
1914
|
+
writeIdentitySnapshot();
|
|
1699
1915
|
},
|
|
1700
1916
|
get projectId() {
|
|
1701
1917
|
if (!_configuredProjectId) throw new AmbaApiError("NOT_INITIALIZED", "amba SDK not configured. Call Amba.configure({ projectId, clientKey }) at app start before any other API.");
|
|
@@ -1848,6 +2064,10 @@ const Amba = {
|
|
|
1848
2064
|
signOut: async (rotateAnonymousId = false) => {
|
|
1849
2065
|
await coreCall((c) => c.signOut(rotateAnonymousId));
|
|
1850
2066
|
_currentSessionToken = null;
|
|
2067
|
+
_currentRefreshToken = null;
|
|
2068
|
+
_currentSessionExpiresAt = null;
|
|
2069
|
+
_currentUser = null;
|
|
2070
|
+
writeIdentitySnapshot();
|
|
1851
2071
|
notifyAuthSubscribers(null);
|
|
1852
2072
|
},
|
|
1853
2073
|
refresh: async () => {
|
|
@@ -1856,11 +2076,17 @@ const Amba = {
|
|
|
1856
2076
|
return r;
|
|
1857
2077
|
},
|
|
1858
2078
|
me: async () => {
|
|
1859
|
-
|
|
2079
|
+
const user = await coreCall((c) => c.me());
|
|
2080
|
+
_currentUser = user;
|
|
2081
|
+
writeIdentitySnapshot();
|
|
2082
|
+
return user;
|
|
1860
2083
|
},
|
|
1861
2084
|
getSession: async () => {
|
|
1862
2085
|
if (!requireCore().isAuthenticated()) return null;
|
|
1863
|
-
|
|
2086
|
+
const user = await coreCall((c) => c.me());
|
|
2087
|
+
_currentUser = user;
|
|
2088
|
+
writeIdentitySnapshot();
|
|
2089
|
+
return snapshotSession(user);
|
|
1864
2090
|
},
|
|
1865
2091
|
getAnonymousId: async () => {
|
|
1866
2092
|
return requireCore().anonymousId();
|
|
@@ -1891,6 +2117,14 @@ const Amba = {
|
|
|
1891
2117
|
await coreCall((c) => c.sessionsRevoke(sessionId));
|
|
1892
2118
|
}
|
|
1893
2119
|
},
|
|
2120
|
+
asGroup: (groupId) => {
|
|
2121
|
+
requireCore();
|
|
2122
|
+
return new AmbaGroupScopedClient(groupId);
|
|
2123
|
+
},
|
|
2124
|
+
inGroup: (groupId) => {
|
|
2125
|
+
requireCore();
|
|
2126
|
+
return new AmbaGroupScopedClient(groupId);
|
|
2127
|
+
},
|
|
1894
2128
|
collection: (name) => new CollectionSubscription({
|
|
1895
2129
|
baseUrl: _configuredApiUrl,
|
|
1896
2130
|
authHeaders: realtimeAuthHeaders
|
|
@@ -2229,7 +2463,7 @@ const Amba = {
|
|
|
2229
2463
|
const method = options?.method ?? "POST";
|
|
2230
2464
|
const headers = {
|
|
2231
2465
|
"X-Api-Key": _configuredClientKey,
|
|
2232
|
-
...options?.headers
|
|
2466
|
+
...options?.headers
|
|
2233
2467
|
};
|
|
2234
2468
|
if (_currentSessionToken) headers["Authorization"] = `Bearer ${_currentSessionToken}`;
|
|
2235
2469
|
let requestBody;
|
|
@@ -2440,12 +2674,12 @@ function getCoreWasm() {
|
|
|
2440
2674
|
* `verify-sdk-version-coherence` GH action gates every publish on the
|
|
2441
2675
|
* pair agreeing (and matching the `web-v<X.Y.Z>` tag when tag-triggered).
|
|
2442
2676
|
*/
|
|
2443
|
-
const SDK_VERSION = "4.0.
|
|
2677
|
+
const SDK_VERSION = "4.0.10";
|
|
2444
2678
|
|
|
2445
2679
|
//#endregion
|
|
2446
2680
|
//#region src/browser.ts
|
|
2447
2681
|
setCoreRuntime(init, AmbaCoreWasm);
|
|
2448
2682
|
|
|
2449
2683
|
//#endregion
|
|
2450
|
-
export { Amba, AmbaApiError, CollectionSubscription, ConversationSubscription, GamificationSubscription, SDK_VERSION, getCoreWasm, withOptimistic };
|
|
2684
|
+
export { Amba, AmbaApiError, AmbaGroupScopedClient, CollectionSubscription, ConversationSubscription, GamificationSubscription, SDK_VERSION, getCoreWasm, withOptimistic };
|
|
2451
2685
|
//# sourceMappingURL=index.browser.js.map
|