@nurama/sdk 1.4.0 → 1.4.2
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 +333 -28
- package/dist/browser/nurama-bot-sdk.min.js +1 -1
- package/dist/browser/nurama-sdk.js +333 -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/device.d.ts +16 -1
- package/dist/routes/device.d.ts.map +1 -1
- package/dist/routes/device.js +34 -0
- package/dist/routes/device.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/device.ts +38 -0
- 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();
|
|
@@ -9665,6 +9785,40 @@
|
|
|
9665
9785
|
endpoint: `/v1/devices/${deviceId}`,
|
|
9666
9786
|
sendJWT: true
|
|
9667
9787
|
});
|
|
9788
|
+
},
|
|
9789
|
+
/**
|
|
9790
|
+
* Register or refresh an iOS upload Live Activity, so the API carries it on by push through post-processing
|
|
9791
|
+
* while the app is suspended. Send the activity's push token and the app's view of the batch; send it again
|
|
9792
|
+
* as the batch changes. Requires authentication.
|
|
9793
|
+
* @param {string} activityId - ActivityKit's id for the activity.
|
|
9794
|
+
* @param {RegisterLiveActivityBody} body - Push token, APNs environment and the batch's files.
|
|
9795
|
+
* @returns {Promise<LiveActivityRegistration>} The activity id and the batch's stage.
|
|
9796
|
+
*/
|
|
9797
|
+
async registerLiveActivity(activityId, body) {
|
|
9798
|
+
if (!activityId) {
|
|
9799
|
+
throw new Error("activityId is required.");
|
|
9800
|
+
}
|
|
9801
|
+
return client._request({
|
|
9802
|
+
method: "PUT",
|
|
9803
|
+
endpoint: `/v1/devices/live-activities/${activityId}`,
|
|
9804
|
+
body,
|
|
9805
|
+
sendJWT: true
|
|
9806
|
+
});
|
|
9807
|
+
},
|
|
9808
|
+
/**
|
|
9809
|
+
* Stop following an upload Live Activity (the app ended it). Requires authentication.
|
|
9810
|
+
* @param {string} activityId - ActivityKit's id for the activity.
|
|
9811
|
+
* @returns {Promise<void>}
|
|
9812
|
+
*/
|
|
9813
|
+
async endLiveActivity(activityId) {
|
|
9814
|
+
if (!activityId) {
|
|
9815
|
+
throw new Error("activityId is required.");
|
|
9816
|
+
}
|
|
9817
|
+
return client._request({
|
|
9818
|
+
method: "DELETE",
|
|
9819
|
+
endpoint: `/v1/devices/live-activities/${activityId}`,
|
|
9820
|
+
sendJWT: true
|
|
9821
|
+
});
|
|
9668
9822
|
}
|
|
9669
9823
|
};
|
|
9670
9824
|
}
|
|
@@ -10331,6 +10485,10 @@
|
|
|
10331
10485
|
/**
|
|
10332
10486
|
* Revoke the bot's current API key and issue a new one.
|
|
10333
10487
|
* The new raw key is returned once.
|
|
10488
|
+
*
|
|
10489
|
+
* The replacement carries the old key's scopes verbatim, so this is not a
|
|
10490
|
+
* way to pick up scopes added to the registry since the key was minted —
|
|
10491
|
+
* `updateBotKeyScopes` is.
|
|
10334
10492
|
*/
|
|
10335
10493
|
async rotateBotKey(workspaceId, botId) {
|
|
10336
10494
|
if (!workspaceId) throw new Error("workspaceId is required.");
|
|
@@ -10341,6 +10499,65 @@
|
|
|
10341
10499
|
sendJWT: true
|
|
10342
10500
|
});
|
|
10343
10501
|
},
|
|
10502
|
+
/**
|
|
10503
|
+
* Mint an ADDITIONAL API key for the bot, up to 5 live keys.
|
|
10504
|
+
*
|
|
10505
|
+
* Unlike `rotateBotKey`, the bot's existing keys keep working — which is
|
|
10506
|
+
* what makes a zero-downtime changeover possible: mint, move traffic over,
|
|
10507
|
+
* then revoke the old key. The raw key is returned once.
|
|
10508
|
+
*/
|
|
10509
|
+
async createBotKey(workspaceId, botId, keyData) {
|
|
10510
|
+
if (!workspaceId) throw new Error("workspaceId is required.");
|
|
10511
|
+
if (!botId) throw new Error("botId is required.");
|
|
10512
|
+
if (!keyData?.name) throw new Error("name is required.");
|
|
10513
|
+
return client._request({
|
|
10514
|
+
method: "POST",
|
|
10515
|
+
endpoint: `/v1/workspaces/${workspaceId}/bots/${botId}/keys`,
|
|
10516
|
+
body: keyData,
|
|
10517
|
+
sendJWT: true
|
|
10518
|
+
});
|
|
10519
|
+
},
|
|
10520
|
+
/**
|
|
10521
|
+
* Replace the scopes on one of the bot's API keys, in place.
|
|
10522
|
+
*
|
|
10523
|
+
* A key's scopes are a snapshot taken when it was minted, and the registry
|
|
10524
|
+
* grows — a key created before a scope existed cannot hold it. Rotation is
|
|
10525
|
+
* not a substitute: `rotateBotKey` copies the old scope set onto the
|
|
10526
|
+
* replacement, so a stale key stays stale through every rotation.
|
|
10527
|
+
*
|
|
10528
|
+
* This matters most for keys minted with `scopes` omitted, which means
|
|
10529
|
+
* "the full registry" as it stood that day, not "whatever the full registry
|
|
10530
|
+
* becomes".
|
|
10531
|
+
*
|
|
10532
|
+
* The secret is unchanged — every caller configured with this key keeps
|
|
10533
|
+
* working, which is the whole point. Requires `canManageBots`.
|
|
10534
|
+
*/
|
|
10535
|
+
async updateBotKeyScopes(workspaceId, botId, keyId, data) {
|
|
10536
|
+
if (!workspaceId) throw new Error("workspaceId is required.");
|
|
10537
|
+
if (!botId) throw new Error("botId is required.");
|
|
10538
|
+
if (!keyId) throw new Error("keyId is required.");
|
|
10539
|
+
return client._request({
|
|
10540
|
+
method: "PATCH",
|
|
10541
|
+
endpoint: `/v1/workspaces/${workspaceId}/bots/${botId}/keys/${keyId}`,
|
|
10542
|
+
body: data,
|
|
10543
|
+
sendJWT: true
|
|
10544
|
+
});
|
|
10545
|
+
},
|
|
10546
|
+
/**
|
|
10547
|
+
* Revoke one of the bot's API keys, leaving its others working.
|
|
10548
|
+
* A revoked key stops authenticating immediately and frees a slot
|
|
10549
|
+
* against the 5-key cap.
|
|
10550
|
+
*/
|
|
10551
|
+
async revokeBotKey(workspaceId, botId, keyId) {
|
|
10552
|
+
if (!workspaceId) throw new Error("workspaceId is required.");
|
|
10553
|
+
if (!botId) throw new Error("botId is required.");
|
|
10554
|
+
if (!keyId) throw new Error("keyId is required.");
|
|
10555
|
+
return client._request({
|
|
10556
|
+
method: "DELETE",
|
|
10557
|
+
endpoint: `/v1/workspaces/${workspaceId}/bots/${botId}/keys/${keyId}`,
|
|
10558
|
+
sendJWT: true
|
|
10559
|
+
});
|
|
10560
|
+
},
|
|
10344
10561
|
/**
|
|
10345
10562
|
* Request signed URL data to upload a new avatar for the bot.
|
|
10346
10563
|
* Caller multipart-uploads the file to S3 using the returned `signedUrlData`,
|
|
@@ -10782,6 +10999,27 @@
|
|
|
10782
10999
|
bypassCache: true
|
|
10783
11000
|
});
|
|
10784
11001
|
},
|
|
11002
|
+
/**
|
|
11003
|
+
* Replace the scopes on one of the caller's Personal Access Tokens.
|
|
11004
|
+
*
|
|
11005
|
+
* `scopes` is the complete new set, not a delta. This exists because
|
|
11006
|
+
* the scope registry grows over time: a token minted before a scope
|
|
11007
|
+
* existed cannot hold it, and without this the only remedy is minting
|
|
11008
|
+
* a replacement and reconfiguring everywhere the old one is used.
|
|
11009
|
+
*
|
|
11010
|
+
* The change takes effect on the token's very next request. Scopes
|
|
11011
|
+
* remain a ceiling, never a floor — widening a token never lets it do
|
|
11012
|
+
* anything its owner cannot already do.
|
|
11013
|
+
*/
|
|
11014
|
+
async updateTokenScopes(tokenId, data) {
|
|
11015
|
+
if (!tokenId) throw new Error("tokenId is required.");
|
|
11016
|
+
return client._request({
|
|
11017
|
+
method: "PATCH",
|
|
11018
|
+
endpoint: `/v1/tokens/${tokenId}`,
|
|
11019
|
+
body: data,
|
|
11020
|
+
sendJWT: true
|
|
11021
|
+
});
|
|
11022
|
+
},
|
|
10785
11023
|
/**
|
|
10786
11024
|
* Revoke one of the caller's Personal Access Tokens. The revocation
|
|
10787
11025
|
* is immediate — the token will return 401 on the very next request.
|
|
@@ -10797,6 +11035,38 @@
|
|
|
10797
11035
|
};
|
|
10798
11036
|
}
|
|
10799
11037
|
|
|
11038
|
+
// src/routes/oauthGrant.ts
|
|
11039
|
+
function createOAuthGrantMethods(client) {
|
|
11040
|
+
return {
|
|
11041
|
+
/**
|
|
11042
|
+
* List the applications the caller has authorised, newest first.
|
|
11043
|
+
* Expired grants are omitted by the server.
|
|
11044
|
+
*/
|
|
11045
|
+
async listGrants() {
|
|
11046
|
+
const res = await client._request({
|
|
11047
|
+
method: "GET",
|
|
11048
|
+
endpoint: "/v1/oauth-grants",
|
|
11049
|
+
sendJWT: true,
|
|
11050
|
+
bypassCache: true
|
|
11051
|
+
});
|
|
11052
|
+
return res.results;
|
|
11053
|
+
},
|
|
11054
|
+
/**
|
|
11055
|
+
* Withdraw an application's access: the grant and every token issued
|
|
11056
|
+
* under it. An access token already in flight stays valid until it
|
|
11057
|
+
* expires, so revocation is not instantaneous for a request under way.
|
|
11058
|
+
*/
|
|
11059
|
+
async revokeGrant(grantId) {
|
|
11060
|
+
if (!grantId) throw new Error("grantId is required.");
|
|
11061
|
+
return client._request({
|
|
11062
|
+
method: "DELETE",
|
|
11063
|
+
endpoint: `/v1/oauth-grants/${grantId}`,
|
|
11064
|
+
sendJWT: true
|
|
11065
|
+
});
|
|
11066
|
+
}
|
|
11067
|
+
};
|
|
11068
|
+
}
|
|
11069
|
+
|
|
10800
11070
|
// src/routes/webhook.ts
|
|
10801
11071
|
function createWebhookMethods(client) {
|
|
10802
11072
|
return {
|
|
@@ -10989,10 +11259,10 @@
|
|
|
10989
11259
|
|
|
10990
11260
|
// src/version.ts
|
|
10991
11261
|
var SDK_VERSION = {
|
|
10992
|
-
version: "1.4.
|
|
10993
|
-
buildTimestamp: "2026-10-
|
|
10994
|
-
buildHash: "
|
|
10995
|
-
gitCommit: "
|
|
11262
|
+
version: "1.4.2",
|
|
11263
|
+
buildTimestamp: "2026-10-13T14:29:32.506Z",
|
|
11264
|
+
buildHash: "4d2cf795aed2effc13ea74e85309a692",
|
|
11265
|
+
gitCommit: "6c603cf",
|
|
10996
11266
|
dirty: false
|
|
10997
11267
|
};
|
|
10998
11268
|
|
|
@@ -11048,6 +11318,7 @@
|
|
|
11048
11318
|
this.invalidateCacheOnMutation = options.invalidateCacheOnMutation === void 0 ? true : options.invalidateCacheOnMutation;
|
|
11049
11319
|
this._onUnauthorized = options.onUnauthorized;
|
|
11050
11320
|
this._onMaintenance = options.onMaintenance;
|
|
11321
|
+
this._onTokensChanged = options.onTokensChanged;
|
|
11051
11322
|
this._apiKey = options.apiKey || null;
|
|
11052
11323
|
if (this.browserMode && !this._apiKey) {
|
|
11053
11324
|
this._loadTokens();
|
|
@@ -11087,6 +11358,7 @@
|
|
|
11087
11358
|
this.credits = createCreditsMethods(this);
|
|
11088
11359
|
this.scratch = createScratchMethods(this);
|
|
11089
11360
|
this.token = createTokenMethods(this);
|
|
11361
|
+
this.oauthGrant = createOAuthGrantMethods(this);
|
|
11090
11362
|
this.webhook = createWebhookMethods(this);
|
|
11091
11363
|
this.supportTicket = createSupportTicketMethods(this);
|
|
11092
11364
|
}
|
|
@@ -11326,6 +11598,39 @@
|
|
|
11326
11598
|
console.error("Failed to save tokens to localStorage:", e);
|
|
11327
11599
|
}
|
|
11328
11600
|
}
|
|
11601
|
+
if (this._onTokensChanged) {
|
|
11602
|
+
try {
|
|
11603
|
+
this._onTokensChanged(accessToken || refreshToken ? this.getSession() : null);
|
|
11604
|
+
} catch (e) {
|
|
11605
|
+
console.error("onTokensChanged handler threw:", e);
|
|
11606
|
+
}
|
|
11607
|
+
}
|
|
11608
|
+
}
|
|
11609
|
+
/**
|
|
11610
|
+
* Returns the current session (tokens and expiries), or null when signed out.
|
|
11611
|
+
* @public
|
|
11612
|
+
*/
|
|
11613
|
+
getSession() {
|
|
11614
|
+
if (!this._accessToken && !this._refreshTokenValue) return null;
|
|
11615
|
+
return {
|
|
11616
|
+
accessToken: this._accessToken,
|
|
11617
|
+
refreshToken: this._refreshTokenValue,
|
|
11618
|
+
accessTokenExpiry: this._accessTokenExpiry,
|
|
11619
|
+
refreshTokenExpiry: this._refreshTokenExpiry
|
|
11620
|
+
};
|
|
11621
|
+
}
|
|
11622
|
+
/**
|
|
11623
|
+
* Restores a session previously captured through `onTokensChanged` /
|
|
11624
|
+
* `getSession()` — for hosts that persist tokens outside localStorage.
|
|
11625
|
+
* Does not fire `onTokensChanged`. A missing or expired access token is
|
|
11626
|
+
* refreshed on the next authenticated request.
|
|
11627
|
+
* @public
|
|
11628
|
+
*/
|
|
11629
|
+
restoreSession(session) {
|
|
11630
|
+
this._accessToken = session?.accessToken ?? null;
|
|
11631
|
+
this._refreshTokenValue = session?.refreshToken ?? null;
|
|
11632
|
+
this._accessTokenExpiry = session?.accessTokenExpiry ?? null;
|
|
11633
|
+
this._refreshTokenExpiry = session?.refreshTokenExpiry ?? null;
|
|
11329
11634
|
}
|
|
11330
11635
|
/**
|
|
11331
11636
|
* Retrieves the current access token.
|