@nurama/sdk 1.4.0 → 1.4.1
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/NuramaClient.d.ts +32 -0
- package/dist/NuramaClient.d.ts.map +1 -1
- package/dist/NuramaClient.js +38 -0
- package/dist/NuramaClient.js.map +1 -1
- package/dist/browser/nurama-bot-sdk.js +299 -28
- package/dist/browser/nurama-bot-sdk.min.js +1 -1
- package/dist/browser/nurama-sdk.js +299 -28
- package/dist/browser/nurama-sdk.min.js +1 -1
- package/dist/routes/bot.d.ts +60 -5
- package/dist/routes/bot.d.ts.map +1 -1
- package/dist/routes/bot.js +72 -0
- package/dist/routes/bot.js.map +1 -1
- package/dist/routes/joinLink.d.ts +51 -1
- package/dist/routes/joinLink.d.ts.map +1 -1
- package/dist/routes/joinLink.js +124 -0
- package/dist/routes/joinLink.js.map +1 -1
- package/dist/routes/oauthGrant.d.ts +45 -0
- package/dist/routes/oauthGrant.d.ts.map +1 -0
- package/dist/routes/oauthGrant.js +32 -0
- package/dist/routes/oauthGrant.js.map +1 -0
- package/dist/routes/socket.d.ts.map +1 -1
- package/dist/routes/socket.js +47 -29
- package/dist/routes/socket.js.map +1 -1
- package/dist/routes/token.d.ts +23 -1
- package/dist/routes/token.d.ts.map +1 -1
- package/dist/routes/token.js +22 -0
- package/dist/routes/token.js.map +1 -1
- package/dist/version.js +4 -4
- package/package.json +2 -2
- package/src/NuramaClient.ts +60 -0
- package/src/routes/bot.ts +104 -5
- package/src/routes/joinLink.ts +133 -0
- package/src/routes/oauthGrant.ts +64 -0
- package/src/routes/socket.ts +51 -33
- package/src/routes/token.ts +40 -1
- package/src/version.ts +4 -4
|
@@ -7880,6 +7880,118 @@
|
|
|
7880
7880
|
sendJWT: true
|
|
7881
7881
|
});
|
|
7882
7882
|
},
|
|
7883
|
+
/** The queue of people waiting on a decision for this project. */
|
|
7884
|
+
async getJoinRequests(params) {
|
|
7885
|
+
if (!params?.projectId) throw new Error("projectId is required.");
|
|
7886
|
+
return client._request({
|
|
7887
|
+
method: "GET",
|
|
7888
|
+
endpoint: "/v1/join-links/requests",
|
|
7889
|
+
params,
|
|
7890
|
+
sendJWT: true
|
|
7891
|
+
});
|
|
7892
|
+
},
|
|
7893
|
+
/**
|
|
7894
|
+
* The caller's own requests still waiting on a decision.
|
|
7895
|
+
*
|
|
7896
|
+
* Needs no `projectId`: it is scoped server-side to the caller, who is not
|
|
7897
|
+
* a member of the projects they are waiting on and so cannot read those
|
|
7898
|
+
* projects' queues.
|
|
7899
|
+
*/
|
|
7900
|
+
async getMyJoinRequests() {
|
|
7901
|
+
return client._request({
|
|
7902
|
+
method: "GET",
|
|
7903
|
+
endpoint: "/v1/join-links/requests/mine",
|
|
7904
|
+
sendJWT: true
|
|
7905
|
+
});
|
|
7906
|
+
},
|
|
7907
|
+
/**
|
|
7908
|
+
* Approve a request and admit the person.
|
|
7909
|
+
*
|
|
7910
|
+
* Seats and the link's remaining uses are re-checked here, not when the
|
|
7911
|
+
* request was made — a queue outlives the capacity that existed when it was
|
|
7912
|
+
* filled, so approval can legitimately fail on a seat limit.
|
|
7913
|
+
*/
|
|
7914
|
+
async approveJoinRequest(requestId, projectId) {
|
|
7915
|
+
if (!requestId) throw new Error("requestId is required.");
|
|
7916
|
+
if (!projectId) throw new Error("projectId is required.");
|
|
7917
|
+
return client._request({
|
|
7918
|
+
method: "POST",
|
|
7919
|
+
endpoint: `/v1/join-links/requests/${requestId}/approve`,
|
|
7920
|
+
body: { projectId },
|
|
7921
|
+
sendJWT: true
|
|
7922
|
+
});
|
|
7923
|
+
},
|
|
7924
|
+
/**
|
|
7925
|
+
* Reject a request, optionally barring the person.
|
|
7926
|
+
*
|
|
7927
|
+
* Passing `ban` is destructive beyond the refusal: a ban REMOVES an existing
|
|
7928
|
+
* membership, so it can take away access the person already had in scope.
|
|
7929
|
+
* It is a separate field for that reason, never implied by the rejection.
|
|
7930
|
+
*/
|
|
7931
|
+
async rejectJoinRequest(requestId, data) {
|
|
7932
|
+
if (!requestId) throw new Error("requestId is required.");
|
|
7933
|
+
if (!data?.projectId) throw new Error("projectId is required.");
|
|
7934
|
+
return client._request({
|
|
7935
|
+
method: "POST",
|
|
7936
|
+
endpoint: `/v1/join-links/requests/${requestId}/reject`,
|
|
7937
|
+
body: data,
|
|
7938
|
+
sendJWT: true
|
|
7939
|
+
});
|
|
7940
|
+
},
|
|
7941
|
+
/**
|
|
7942
|
+
* Change or lift the cooldown on a rejected request.
|
|
7943
|
+
*
|
|
7944
|
+
* Only a rejected request has one; `reapplyAfter: null` lifts it, letting
|
|
7945
|
+
* the person ask again straight away. A barred person is refused — the ban
|
|
7946
|
+
* already blocks every path, so lifting that is the action that matters.
|
|
7947
|
+
*/
|
|
7948
|
+
async updateJoinRequestCooldown(requestId, data) {
|
|
7949
|
+
if (!requestId) throw new Error("requestId is required.");
|
|
7950
|
+
if (!data?.projectId) throw new Error("projectId is required.");
|
|
7951
|
+
return client._request({
|
|
7952
|
+
method: "PATCH",
|
|
7953
|
+
endpoint: `/v1/join-links/requests/${requestId}/cooldown`,
|
|
7954
|
+
body: { projectId: data.projectId, reapplyAfter: data.reapplyAfter ?? null },
|
|
7955
|
+
sendJWT: true
|
|
7956
|
+
});
|
|
7957
|
+
},
|
|
7958
|
+
/**
|
|
7959
|
+
* Bar someone, without a request to refuse.
|
|
7960
|
+
*
|
|
7961
|
+
* Destructive: the ban REMOVES every membership the person holds in its
|
|
7962
|
+
* scope, so a `workspace` ban clears them out of every project in it. An
|
|
7963
|
+
* owner cannot be barred, and neither can you bar yourself.
|
|
7964
|
+
*/
|
|
7965
|
+
async createResourceBan(data) {
|
|
7966
|
+
if (!data?.projectId) throw new Error("projectId is required.");
|
|
7967
|
+
if (!data?.userId) throw new Error("userId is required.");
|
|
7968
|
+
if (!data?.scope) throw new Error("scope is required.");
|
|
7969
|
+
return client._request({
|
|
7970
|
+
method: "POST",
|
|
7971
|
+
endpoint: "/v1/join-links/bans",
|
|
7972
|
+
body: data,
|
|
7973
|
+
sendJWT: true
|
|
7974
|
+
});
|
|
7975
|
+
},
|
|
7976
|
+
/** People barred from this project, newest first. */
|
|
7977
|
+
async getResourceBans(params) {
|
|
7978
|
+
if (!params?.projectId) throw new Error("projectId is required.");
|
|
7979
|
+
return client._request({
|
|
7980
|
+
method: "GET",
|
|
7981
|
+
endpoint: "/v1/join-links/bans",
|
|
7982
|
+
params,
|
|
7983
|
+
sendJWT: true
|
|
7984
|
+
});
|
|
7985
|
+
},
|
|
7986
|
+
/** Lift a ban. It does not restore any membership the ban removed. */
|
|
7987
|
+
async liftResourceBan(banId) {
|
|
7988
|
+
if (!banId) throw new Error("banId is required.");
|
|
7989
|
+
return client._request({
|
|
7990
|
+
method: "DELETE",
|
|
7991
|
+
endpoint: `/v1/join-links/bans/${banId}`,
|
|
7992
|
+
sendJWT: true
|
|
7993
|
+
});
|
|
7994
|
+
},
|
|
7883
7995
|
/** Update a join link's settings (role, mode, domains, expiry, enable/disable). */
|
|
7884
7996
|
async updateJoinLink(linkId, data) {
|
|
7885
7997
|
if (!linkId) throw new Error("linkId is required.");
|
|
@@ -8306,6 +8418,19 @@
|
|
|
8306
8418
|
"reconnect_failed",
|
|
8307
8419
|
"reconnect"
|
|
8308
8420
|
]);
|
|
8421
|
+
var RECONNECTION_DELAY = 1e3;
|
|
8422
|
+
var RECONNECTION_DELAY_MAX = 3e4;
|
|
8423
|
+
var SERVER_CLOSE_RECONNECT_SPREAD = 1e4;
|
|
8424
|
+
var spreadReconnectAfterServerClose = (socket) => {
|
|
8425
|
+
const manager = socket.io;
|
|
8426
|
+
if (typeof manager?.reconnectionDelay !== "function") return;
|
|
8427
|
+
socket.on("disconnect", (reason) => {
|
|
8428
|
+
if (reason === "transport close") {
|
|
8429
|
+
manager.reconnectionDelay(RECONNECTION_DELAY + Math.random() * SERVER_CLOSE_RECONNECT_SPREAD);
|
|
8430
|
+
}
|
|
8431
|
+
});
|
|
8432
|
+
socket.on("connect", () => manager.reconnectionDelay(RECONNECTION_DELAY));
|
|
8433
|
+
};
|
|
8309
8434
|
function createSocketMethods(client) {
|
|
8310
8435
|
const activeConnections = {};
|
|
8311
8436
|
const debug = client.debug;
|
|
@@ -8359,12 +8484,12 @@
|
|
|
8359
8484
|
query: { token },
|
|
8360
8485
|
// Reconnection configuration for resilience
|
|
8361
8486
|
reconnection: socketOptions.autoReconnect,
|
|
8362
|
-
|
|
8363
|
-
//
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8487
|
+
// Never give up: the server may refuse connections for a while (busy, or a deploy draining), and after a
|
|
8488
|
+
// final failure socket.io stops for good without telling the app (the manager's 'reconnect_failed' never
|
|
8489
|
+
// reaches socket listeners). Retries settle at one every ~30s.
|
|
8490
|
+
reconnectionAttempts: Infinity,
|
|
8491
|
+
reconnectionDelay: RECONNECTION_DELAY,
|
|
8492
|
+
reconnectionDelayMax: RECONNECTION_DELAY_MAX,
|
|
8368
8493
|
randomizationFactor: 0.5,
|
|
8369
8494
|
// Add jitter to avoid thundering herd problem
|
|
8370
8495
|
// Connection timeout
|
|
@@ -8387,11 +8512,19 @@
|
|
|
8387
8512
|
listeners: {}
|
|
8388
8513
|
};
|
|
8389
8514
|
activeConnections[channel] = socketChannel;
|
|
8515
|
+
spreadReconnectAfterServerClose(socket);
|
|
8390
8516
|
return new Promise((resolve, reject) => {
|
|
8391
8517
|
let settled = false;
|
|
8518
|
+
let hasConnected = false;
|
|
8392
8519
|
socket.on("connect", () => {
|
|
8393
8520
|
_log(`Connected to channel: ${channel}`);
|
|
8394
8521
|
activeConnections[channel] = socketChannel;
|
|
8522
|
+
if (hasConnected) {
|
|
8523
|
+
_log(`Reconnected to channel ${channel}`);
|
|
8524
|
+
const reconnectListeners = socketChannel.listeners["reconnect"] || /* @__PURE__ */ new Set();
|
|
8525
|
+
reconnectListeners.forEach((listener) => listener({ type: "reconnect" }));
|
|
8526
|
+
}
|
|
8527
|
+
hasConnected = true;
|
|
8395
8528
|
if (!settled) {
|
|
8396
8529
|
settled = true;
|
|
8397
8530
|
resolve(socketChannel);
|
|
@@ -8404,15 +8537,6 @@
|
|
|
8404
8537
|
resolve(socketChannel);
|
|
8405
8538
|
}
|
|
8406
8539
|
});
|
|
8407
|
-
socket.on("reconnect_attempt", (attempt) => {
|
|
8408
|
-
_log(`Reconnection attempt ${attempt} for channel: ${channel}`);
|
|
8409
|
-
});
|
|
8410
|
-
socket.on("reconnect", (attempt) => {
|
|
8411
|
-
_log(`Reconnected to channel ${channel} after ${attempt} attempts`);
|
|
8412
|
-
activeConnections[channel] = socketChannel;
|
|
8413
|
-
const reconnectListeners = socketChannel.listeners["reconnect"] || /* @__PURE__ */ new Set();
|
|
8414
|
-
reconnectListeners.forEach((listener) => listener({ type: "reconnect", attempt }));
|
|
8415
|
-
});
|
|
8416
8540
|
socket.on("reconnect_failed", () => {
|
|
8417
8541
|
_log(`Reconnection failed for channel ${channel} after maximum attempts`);
|
|
8418
8542
|
const eventListeners = socketChannel.listeners["reconnect_failed"] || /* @__PURE__ */ new Set();
|
|
@@ -8492,9 +8616,10 @@
|
|
|
8492
8616
|
query: { publicToken },
|
|
8493
8617
|
// Reconnection configuration for resilience
|
|
8494
8618
|
reconnection: socketOptions.autoReconnect,
|
|
8495
|
-
reconnectionAttempts:
|
|
8496
|
-
|
|
8497
|
-
|
|
8619
|
+
reconnectionAttempts: Infinity,
|
|
8620
|
+
// as connect(): never give up silently
|
|
8621
|
+
reconnectionDelay: RECONNECTION_DELAY,
|
|
8622
|
+
reconnectionDelayMax: RECONNECTION_DELAY_MAX,
|
|
8498
8623
|
randomizationFactor: 0.5,
|
|
8499
8624
|
// Connection timeout
|
|
8500
8625
|
timeout: 2e4,
|
|
@@ -8509,6 +8634,7 @@
|
|
|
8509
8634
|
channel,
|
|
8510
8635
|
listeners: {}
|
|
8511
8636
|
};
|
|
8637
|
+
spreadReconnectAfterServerClose(socket);
|
|
8512
8638
|
return new Promise((resolve, reject) => {
|
|
8513
8639
|
socket.on("connect", () => {
|
|
8514
8640
|
_log(`Connected to public channel: ${channel}`);
|
|
@@ -8519,12 +8645,6 @@
|
|
|
8519
8645
|
_log(`Connection error for public channel ${channel}:`, error);
|
|
8520
8646
|
reject(error);
|
|
8521
8647
|
});
|
|
8522
|
-
socket.on("reconnect_attempt", (attempt) => {
|
|
8523
|
-
_log(`Reconnection attempt ${attempt} for public channel: ${channel}`);
|
|
8524
|
-
});
|
|
8525
|
-
socket.on("reconnect", (attempt) => {
|
|
8526
|
-
_log(`Reconnected to public channel ${channel} after ${attempt} attempts`);
|
|
8527
|
-
});
|
|
8528
8648
|
socket.on("reconnect_failed", () => {
|
|
8529
8649
|
_log(`Reconnection failed for public channel ${channel} after maximum attempts`);
|
|
8530
8650
|
const eventListeners = socketChannel.listeners["reconnect_failed"] || /* @__PURE__ */ new Set();
|
|
@@ -10331,6 +10451,10 @@
|
|
|
10331
10451
|
/**
|
|
10332
10452
|
* Revoke the bot's current API key and issue a new one.
|
|
10333
10453
|
* The new raw key is returned once.
|
|
10454
|
+
*
|
|
10455
|
+
* The replacement carries the old key's scopes verbatim, so this is not a
|
|
10456
|
+
* way to pick up scopes added to the registry since the key was minted —
|
|
10457
|
+
* `updateBotKeyScopes` is.
|
|
10334
10458
|
*/
|
|
10335
10459
|
async rotateBotKey(workspaceId, botId) {
|
|
10336
10460
|
if (!workspaceId) throw new Error("workspaceId is required.");
|
|
@@ -10341,6 +10465,65 @@
|
|
|
10341
10465
|
sendJWT: true
|
|
10342
10466
|
});
|
|
10343
10467
|
},
|
|
10468
|
+
/**
|
|
10469
|
+
* Mint an ADDITIONAL API key for the bot, up to 5 live keys.
|
|
10470
|
+
*
|
|
10471
|
+
* Unlike `rotateBotKey`, the bot's existing keys keep working — which is
|
|
10472
|
+
* what makes a zero-downtime changeover possible: mint, move traffic over,
|
|
10473
|
+
* then revoke the old key. The raw key is returned once.
|
|
10474
|
+
*/
|
|
10475
|
+
async createBotKey(workspaceId, botId, keyData) {
|
|
10476
|
+
if (!workspaceId) throw new Error("workspaceId is required.");
|
|
10477
|
+
if (!botId) throw new Error("botId is required.");
|
|
10478
|
+
if (!keyData?.name) throw new Error("name is required.");
|
|
10479
|
+
return client._request({
|
|
10480
|
+
method: "POST",
|
|
10481
|
+
endpoint: `/v1/workspaces/${workspaceId}/bots/${botId}/keys`,
|
|
10482
|
+
body: keyData,
|
|
10483
|
+
sendJWT: true
|
|
10484
|
+
});
|
|
10485
|
+
},
|
|
10486
|
+
/**
|
|
10487
|
+
* Replace the scopes on one of the bot's API keys, in place.
|
|
10488
|
+
*
|
|
10489
|
+
* A key's scopes are a snapshot taken when it was minted, and the registry
|
|
10490
|
+
* grows — a key created before a scope existed cannot hold it. Rotation is
|
|
10491
|
+
* not a substitute: `rotateBotKey` copies the old scope set onto the
|
|
10492
|
+
* replacement, so a stale key stays stale through every rotation.
|
|
10493
|
+
*
|
|
10494
|
+
* This matters most for keys minted with `scopes` omitted, which means
|
|
10495
|
+
* "the full registry" as it stood that day, not "whatever the full registry
|
|
10496
|
+
* becomes".
|
|
10497
|
+
*
|
|
10498
|
+
* The secret is unchanged — every caller configured with this key keeps
|
|
10499
|
+
* working, which is the whole point. Requires `canManageBots`.
|
|
10500
|
+
*/
|
|
10501
|
+
async updateBotKeyScopes(workspaceId, botId, keyId, data) {
|
|
10502
|
+
if (!workspaceId) throw new Error("workspaceId is required.");
|
|
10503
|
+
if (!botId) throw new Error("botId is required.");
|
|
10504
|
+
if (!keyId) throw new Error("keyId is required.");
|
|
10505
|
+
return client._request({
|
|
10506
|
+
method: "PATCH",
|
|
10507
|
+
endpoint: `/v1/workspaces/${workspaceId}/bots/${botId}/keys/${keyId}`,
|
|
10508
|
+
body: data,
|
|
10509
|
+
sendJWT: true
|
|
10510
|
+
});
|
|
10511
|
+
},
|
|
10512
|
+
/**
|
|
10513
|
+
* Revoke one of the bot's API keys, leaving its others working.
|
|
10514
|
+
* A revoked key stops authenticating immediately and frees a slot
|
|
10515
|
+
* against the 5-key cap.
|
|
10516
|
+
*/
|
|
10517
|
+
async revokeBotKey(workspaceId, botId, keyId) {
|
|
10518
|
+
if (!workspaceId) throw new Error("workspaceId is required.");
|
|
10519
|
+
if (!botId) throw new Error("botId is required.");
|
|
10520
|
+
if (!keyId) throw new Error("keyId is required.");
|
|
10521
|
+
return client._request({
|
|
10522
|
+
method: "DELETE",
|
|
10523
|
+
endpoint: `/v1/workspaces/${workspaceId}/bots/${botId}/keys/${keyId}`,
|
|
10524
|
+
sendJWT: true
|
|
10525
|
+
});
|
|
10526
|
+
},
|
|
10344
10527
|
/**
|
|
10345
10528
|
* Request signed URL data to upload a new avatar for the bot.
|
|
10346
10529
|
* Caller multipart-uploads the file to S3 using the returned `signedUrlData`,
|
|
@@ -10782,6 +10965,27 @@
|
|
|
10782
10965
|
bypassCache: true
|
|
10783
10966
|
});
|
|
10784
10967
|
},
|
|
10968
|
+
/**
|
|
10969
|
+
* Replace the scopes on one of the caller's Personal Access Tokens.
|
|
10970
|
+
*
|
|
10971
|
+
* `scopes` is the complete new set, not a delta. This exists because
|
|
10972
|
+
* the scope registry grows over time: a token minted before a scope
|
|
10973
|
+
* existed cannot hold it, and without this the only remedy is minting
|
|
10974
|
+
* a replacement and reconfiguring everywhere the old one is used.
|
|
10975
|
+
*
|
|
10976
|
+
* The change takes effect on the token's very next request. Scopes
|
|
10977
|
+
* remain a ceiling, never a floor — widening a token never lets it do
|
|
10978
|
+
* anything its owner cannot already do.
|
|
10979
|
+
*/
|
|
10980
|
+
async updateTokenScopes(tokenId, data) {
|
|
10981
|
+
if (!tokenId) throw new Error("tokenId is required.");
|
|
10982
|
+
return client._request({
|
|
10983
|
+
method: "PATCH",
|
|
10984
|
+
endpoint: `/v1/tokens/${tokenId}`,
|
|
10985
|
+
body: data,
|
|
10986
|
+
sendJWT: true
|
|
10987
|
+
});
|
|
10988
|
+
},
|
|
10785
10989
|
/**
|
|
10786
10990
|
* Revoke one of the caller's Personal Access Tokens. The revocation
|
|
10787
10991
|
* is immediate — the token will return 401 on the very next request.
|
|
@@ -10797,6 +11001,38 @@
|
|
|
10797
11001
|
};
|
|
10798
11002
|
}
|
|
10799
11003
|
|
|
11004
|
+
// src/routes/oauthGrant.ts
|
|
11005
|
+
function createOAuthGrantMethods(client) {
|
|
11006
|
+
return {
|
|
11007
|
+
/**
|
|
11008
|
+
* List the applications the caller has authorised, newest first.
|
|
11009
|
+
* Expired grants are omitted by the server.
|
|
11010
|
+
*/
|
|
11011
|
+
async listGrants() {
|
|
11012
|
+
const res = await client._request({
|
|
11013
|
+
method: "GET",
|
|
11014
|
+
endpoint: "/v1/oauth-grants",
|
|
11015
|
+
sendJWT: true,
|
|
11016
|
+
bypassCache: true
|
|
11017
|
+
});
|
|
11018
|
+
return res.results;
|
|
11019
|
+
},
|
|
11020
|
+
/**
|
|
11021
|
+
* Withdraw an application's access: the grant and every token issued
|
|
11022
|
+
* under it. An access token already in flight stays valid until it
|
|
11023
|
+
* expires, so revocation is not instantaneous for a request under way.
|
|
11024
|
+
*/
|
|
11025
|
+
async revokeGrant(grantId) {
|
|
11026
|
+
if (!grantId) throw new Error("grantId is required.");
|
|
11027
|
+
return client._request({
|
|
11028
|
+
method: "DELETE",
|
|
11029
|
+
endpoint: `/v1/oauth-grants/${grantId}`,
|
|
11030
|
+
sendJWT: true
|
|
11031
|
+
});
|
|
11032
|
+
}
|
|
11033
|
+
};
|
|
11034
|
+
}
|
|
11035
|
+
|
|
10800
11036
|
// src/routes/webhook.ts
|
|
10801
11037
|
function createWebhookMethods(client) {
|
|
10802
11038
|
return {
|
|
@@ -10989,10 +11225,10 @@
|
|
|
10989
11225
|
|
|
10990
11226
|
// src/version.ts
|
|
10991
11227
|
var SDK_VERSION = {
|
|
10992
|
-
version: "1.4.
|
|
10993
|
-
buildTimestamp: "2026-10-
|
|
10994
|
-
buildHash: "
|
|
10995
|
-
gitCommit: "
|
|
11228
|
+
version: "1.4.1",
|
|
11229
|
+
buildTimestamp: "2026-10-10T06:58:40.258Z",
|
|
11230
|
+
buildHash: "f6505e2eb9725dc8d239bb401ced8f88",
|
|
11231
|
+
gitCommit: "1c00e18",
|
|
10996
11232
|
dirty: false
|
|
10997
11233
|
};
|
|
10998
11234
|
|
|
@@ -11048,6 +11284,7 @@
|
|
|
11048
11284
|
this.invalidateCacheOnMutation = options.invalidateCacheOnMutation === void 0 ? true : options.invalidateCacheOnMutation;
|
|
11049
11285
|
this._onUnauthorized = options.onUnauthorized;
|
|
11050
11286
|
this._onMaintenance = options.onMaintenance;
|
|
11287
|
+
this._onTokensChanged = options.onTokensChanged;
|
|
11051
11288
|
this._apiKey = options.apiKey || null;
|
|
11052
11289
|
if (this.browserMode && !this._apiKey) {
|
|
11053
11290
|
this._loadTokens();
|
|
@@ -11087,6 +11324,7 @@
|
|
|
11087
11324
|
this.credits = createCreditsMethods(this);
|
|
11088
11325
|
this.scratch = createScratchMethods(this);
|
|
11089
11326
|
this.token = createTokenMethods(this);
|
|
11327
|
+
this.oauthGrant = createOAuthGrantMethods(this);
|
|
11090
11328
|
this.webhook = createWebhookMethods(this);
|
|
11091
11329
|
this.supportTicket = createSupportTicketMethods(this);
|
|
11092
11330
|
}
|
|
@@ -11326,6 +11564,39 @@
|
|
|
11326
11564
|
console.error("Failed to save tokens to localStorage:", e);
|
|
11327
11565
|
}
|
|
11328
11566
|
}
|
|
11567
|
+
if (this._onTokensChanged) {
|
|
11568
|
+
try {
|
|
11569
|
+
this._onTokensChanged(accessToken || refreshToken ? this.getSession() : null);
|
|
11570
|
+
} catch (e) {
|
|
11571
|
+
console.error("onTokensChanged handler threw:", e);
|
|
11572
|
+
}
|
|
11573
|
+
}
|
|
11574
|
+
}
|
|
11575
|
+
/**
|
|
11576
|
+
* Returns the current session (tokens and expiries), or null when signed out.
|
|
11577
|
+
* @public
|
|
11578
|
+
*/
|
|
11579
|
+
getSession() {
|
|
11580
|
+
if (!this._accessToken && !this._refreshTokenValue) return null;
|
|
11581
|
+
return {
|
|
11582
|
+
accessToken: this._accessToken,
|
|
11583
|
+
refreshToken: this._refreshTokenValue,
|
|
11584
|
+
accessTokenExpiry: this._accessTokenExpiry,
|
|
11585
|
+
refreshTokenExpiry: this._refreshTokenExpiry
|
|
11586
|
+
};
|
|
11587
|
+
}
|
|
11588
|
+
/**
|
|
11589
|
+
* Restores a session previously captured through `onTokensChanged` /
|
|
11590
|
+
* `getSession()` — for hosts that persist tokens outside localStorage.
|
|
11591
|
+
* Does not fire `onTokensChanged`. A missing or expired access token is
|
|
11592
|
+
* refreshed on the next authenticated request.
|
|
11593
|
+
* @public
|
|
11594
|
+
*/
|
|
11595
|
+
restoreSession(session) {
|
|
11596
|
+
this._accessToken = session?.accessToken ?? null;
|
|
11597
|
+
this._refreshTokenValue = session?.refreshToken ?? null;
|
|
11598
|
+
this._accessTokenExpiry = session?.accessTokenExpiry ?? null;
|
|
11599
|
+
this._refreshTokenExpiry = session?.refreshTokenExpiry ?? null;
|
|
11329
11600
|
}
|
|
11330
11601
|
/**
|
|
11331
11602
|
* Retrieves the current access token.
|