@usearete/sdk 0.26.0 → 0.27.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/dist/index.cjs +148 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +49 -3
- package/dist/index.esm.js +144 -8
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -4,6 +4,97 @@ var pako = require('pako');
|
|
|
4
4
|
var buffer = require('buffer');
|
|
5
5
|
|
|
6
6
|
const DEFAULT_MAX_ENTRIES_PER_VIEW = 10000;
|
|
7
|
+
const API_PROBLEM_SCHEMA_VERSION = 1;
|
|
8
|
+
const CLAIM_AGENT_MATERIALIZER_PATH = '/api/agents/me/claim-links';
|
|
9
|
+
function isRecord$1(value) {
|
|
10
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
11
|
+
}
|
|
12
|
+
function isSafeUnsignedInteger(value) {
|
|
13
|
+
return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;
|
|
14
|
+
}
|
|
15
|
+
function parseUsageLimit(value) {
|
|
16
|
+
if (!isRecord$1(value)
|
|
17
|
+
|| typeof value['unit'] !== 'string'
|
|
18
|
+
|| !isSafeUnsignedInteger(value['used'])
|
|
19
|
+
|| !isSafeUnsignedInteger(value['limit'])
|
|
20
|
+
|| (value['resetsAt'] !== undefined
|
|
21
|
+
&& value['resetsAt'] !== null
|
|
22
|
+
&& typeof value['resetsAt'] !== 'string')) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
unit: value['unit'],
|
|
27
|
+
used: value['used'],
|
|
28
|
+
limit: value['limit'],
|
|
29
|
+
...(value['resetsAt'] !== undefined ? { resetsAt: value['resetsAt'] } : {}),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function parseRecoveryAction(value) {
|
|
33
|
+
if (!isRecord$1(value) || typeof value['type'] !== 'string') {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
if ((value['label'] !== undefined && typeof value['label'] !== 'string')
|
|
37
|
+
|| (value['method'] !== undefined && typeof value['method'] !== 'string')
|
|
38
|
+
|| (value['path'] !== undefined && typeof value['path'] !== 'string')) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
function isClaimAgentRecoveryAction(action) {
|
|
44
|
+
return action?.type === 'claim_agent'
|
|
45
|
+
&& action.method === 'POST'
|
|
46
|
+
&& action.path === CLAIM_AGENT_MATERIALIZER_PATH;
|
|
47
|
+
}
|
|
48
|
+
function parseApiProblem(value) {
|
|
49
|
+
if (!isRecord$1(value) || typeof value['error'] !== 'string') {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
if (value['schemaVersion'] !== undefined
|
|
53
|
+
&& (!isSafeUnsignedInteger(value['schemaVersion']) || value['schemaVersion'] < 1)) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
if ((value['code'] !== undefined && typeof value['code'] !== 'string')
|
|
57
|
+
|| (value['retryable'] !== undefined && typeof value['retryable'] !== 'boolean')
|
|
58
|
+
|| (value['requestId'] !== undefined && typeof value['requestId'] !== 'string')
|
|
59
|
+
|| (value['retryAfterSeconds'] !== undefined
|
|
60
|
+
&& value['retryAfterSeconds'] !== null
|
|
61
|
+
&& !isSafeUnsignedInteger(value['retryAfterSeconds']))) {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
const usage = value['usage'] === undefined ? undefined : parseUsageLimit(value['usage']);
|
|
65
|
+
const action = value['action'] === undefined ? undefined : parseRecoveryAction(value['action']);
|
|
66
|
+
if ((value['usage'] !== undefined && !usage) || (value['action'] !== undefined && !action)) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
...value,
|
|
71
|
+
error: value['error'],
|
|
72
|
+
...(usage ? { usage } : {}),
|
|
73
|
+
...(action ? { action } : {}),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function isSafeClaimActionUrl(action, expectedOrigin) {
|
|
77
|
+
if (action.type !== 'claim_agent')
|
|
78
|
+
return false;
|
|
79
|
+
try {
|
|
80
|
+
const url = new URL(action.url);
|
|
81
|
+
const origin = new URL(expectedOrigin);
|
|
82
|
+
const loopback = url.hostname === 'localhost'
|
|
83
|
+
|| url.hostname === '127.0.0.1'
|
|
84
|
+
|| url.hostname === '[::1]';
|
|
85
|
+
const secure = url.protocol === 'https:' || (url.protocol === 'http:' && loopback);
|
|
86
|
+
return secure
|
|
87
|
+
&& url.origin === origin.origin
|
|
88
|
+
&& url.username === ''
|
|
89
|
+
&& url.password === ''
|
|
90
|
+
&& url.pathname === '/claim'
|
|
91
|
+
&& url.search === ''
|
|
92
|
+
&& url.hash.length > 1;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
7
98
|
const DEFAULT_CONFIG = {
|
|
8
99
|
autoReconnect: true,
|
|
9
100
|
reconnectIntervals: [1000, 2000, 4000, 8000, 16000],
|
|
@@ -45,6 +136,17 @@ class AreteError extends Error {
|
|
|
45
136
|
this.details = details;
|
|
46
137
|
this.name = 'AreteError';
|
|
47
138
|
}
|
|
139
|
+
apiProblem() {
|
|
140
|
+
if (!isRecord$1(this.details))
|
|
141
|
+
return undefined;
|
|
142
|
+
return parseApiProblem(this.details['apiProblem']);
|
|
143
|
+
}
|
|
144
|
+
recoveryAction() {
|
|
145
|
+
return this.apiProblem()?.action;
|
|
146
|
+
}
|
|
147
|
+
retryAfter() {
|
|
148
|
+
return this.apiProblem()?.retryAfterSeconds ?? undefined;
|
|
149
|
+
}
|
|
48
150
|
}
|
|
49
151
|
/**
|
|
50
152
|
* Parse a kebab-case error code string (from X-Error-Code header) to AuthErrorCode.
|
|
@@ -109,6 +211,7 @@ const AUTH_ERROR_CODES_BY_WIRE = {
|
|
|
109
211
|
'secret-key-required': 'SECRET_KEY_REQUIRED',
|
|
110
212
|
'deployment-access-denied': 'DEPLOYMENT_ACCESS_DENIED',
|
|
111
213
|
'quota-exceeded': 'QUOTA_EXCEEDED',
|
|
214
|
+
'agent-claim-required': 'AGENT_CLAIM_REQUIRED',
|
|
112
215
|
'stack-version-retired': 'STACK_VERSION_RETIRED',
|
|
113
216
|
'stack-version-unknown': 'STACK_VERSION_UNKNOWN',
|
|
114
217
|
};
|
|
@@ -1187,9 +1290,11 @@ class ConnectionManager {
|
|
|
1187
1290
|
if (!response.ok) {
|
|
1188
1291
|
const rawError = await response.text();
|
|
1189
1292
|
let parsedError;
|
|
1293
|
+
let parsedValue;
|
|
1190
1294
|
if (rawError) {
|
|
1191
1295
|
try {
|
|
1192
|
-
|
|
1296
|
+
parsedValue = JSON.parse(rawError);
|
|
1297
|
+
parsedError = parsedValue;
|
|
1193
1298
|
}
|
|
1194
1299
|
catch {
|
|
1195
1300
|
parsedError = undefined;
|
|
@@ -1205,19 +1310,32 @@ class ConnectionManager {
|
|
|
1205
1310
|
const errorMessage = typeof parsedError?.error === 'string' && parsedError.error.length > 0
|
|
1206
1311
|
? parsedError.error
|
|
1207
1312
|
: rawError || response.statusText || 'Authentication request failed';
|
|
1313
|
+
const retryAfterHeader = response.headers.get('Retry-After');
|
|
1314
|
+
const retryAfterSeconds = retryAfterHeader && /^\d+$/.test(retryAfterHeader)
|
|
1315
|
+
? Number(retryAfterHeader)
|
|
1316
|
+
: undefined;
|
|
1317
|
+
const parsedProblem = parseApiProblem(parsedValue);
|
|
1318
|
+
const apiProblem = parsedProblem
|
|
1319
|
+
? {
|
|
1320
|
+
...parsedProblem,
|
|
1321
|
+
...(retryAfterSeconds !== undefined ? { retryAfterSeconds } : {}),
|
|
1322
|
+
}
|
|
1323
|
+
: undefined;
|
|
1208
1324
|
if (isStackVersionRefusalCode(errorCode)) {
|
|
1209
1325
|
const refusal = parseStackVersionRefusal(parsedError);
|
|
1210
1326
|
throw new AreteError(describeStackVersionRefusal(`Token endpoint returned ${response.status}: ${errorMessage}`, refusal), errorCode, {
|
|
1211
1327
|
status: response.status,
|
|
1212
1328
|
wireErrorCode,
|
|
1213
1329
|
responseBody: rawError || null,
|
|
1330
|
+
apiProblem,
|
|
1214
1331
|
...refusal,
|
|
1215
1332
|
});
|
|
1216
1333
|
}
|
|
1217
1334
|
throw new AreteError(`Token endpoint returned ${response.status}: ${errorMessage}`, errorCode, {
|
|
1218
1335
|
status: response.status,
|
|
1219
1336
|
wireErrorCode,
|
|
1220
|
-
|
|
1337
|
+
apiProblem,
|
|
1338
|
+
retryAfterSeconds,
|
|
1221
1339
|
});
|
|
1222
1340
|
}
|
|
1223
1341
|
const data = (await response.json());
|
|
@@ -1478,6 +1596,8 @@ class ConnectionManager {
|
|
|
1478
1596
|
code: parseWireErrorCode(message.code),
|
|
1479
1597
|
retryable: message.retryable ?? false,
|
|
1480
1598
|
retryAfter: message.retryAfter ?? message.retry_after,
|
|
1599
|
+
usage: message.usage,
|
|
1600
|
+
action: message.action,
|
|
1481
1601
|
suggestedAction: message.suggestedAction ?? message.suggested_action,
|
|
1482
1602
|
docsUrl: message.docsUrl ?? message.docs_url,
|
|
1483
1603
|
fatal: message.fatal,
|
|
@@ -1839,7 +1959,12 @@ class ConnectionManager {
|
|
|
1839
1959
|
* with the `reconnecting` state and kept for the terminal error if the
|
|
1840
1960
|
* attempts run out, so a server's close reason is never dropped.
|
|
1841
1961
|
*/
|
|
1842
|
-
handleReconnect(
|
|
1962
|
+
handleReconnect(error) {
|
|
1963
|
+
const reason = typeof error === 'string'
|
|
1964
|
+
? error
|
|
1965
|
+
: error instanceof Error
|
|
1966
|
+
? error.message
|
|
1967
|
+
: undefined;
|
|
1843
1968
|
if (reason !== undefined) {
|
|
1844
1969
|
this.lastDisconnectReason = reason;
|
|
1845
1970
|
}
|
|
@@ -1847,6 +1972,13 @@ class ConnectionManager {
|
|
|
1847
1972
|
this.updateState('error', 'Automatic reconnection is disabled');
|
|
1848
1973
|
return;
|
|
1849
1974
|
}
|
|
1975
|
+
if (error instanceof AreteError) {
|
|
1976
|
+
const problem = error.apiProblem();
|
|
1977
|
+
if (problem?.action || problem?.retryable === false) {
|
|
1978
|
+
this.updateState('error', error.message);
|
|
1979
|
+
return;
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1850
1982
|
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
1851
1983
|
const lastReason = this.lastDisconnectReason;
|
|
1852
1984
|
this.updateState('error', `Max reconnection attempts (${this.reconnectAttempts}) reached`
|
|
@@ -1855,14 +1987,18 @@ class ConnectionManager {
|
|
|
1855
1987
|
}
|
|
1856
1988
|
this.updateState('reconnecting', this.lastDisconnectReason);
|
|
1857
1989
|
const attemptIndex = Math.min(this.reconnectAttempts, this.reconnectIntervals.length - 1);
|
|
1858
|
-
const
|
|
1990
|
+
const configuredDelay = this.reconnectIntervals[attemptIndex] ?? 1000;
|
|
1991
|
+
const serverDelay = error instanceof AreteError
|
|
1992
|
+
? (error.retryAfter() ?? 0) * 1000
|
|
1993
|
+
: 0;
|
|
1994
|
+
const delay = Math.max(configuredDelay, serverDelay);
|
|
1859
1995
|
this.reconnectAttempts++;
|
|
1860
1996
|
this.reconnectTimeout = setTimeout(() => {
|
|
1861
|
-
this.connect(true).catch((
|
|
1997
|
+
this.connect(true).catch((connectError) => {
|
|
1862
1998
|
// Once a socket exists, its close event owns the next retry. Token
|
|
1863
1999
|
// acquisition and socket construction can fail before that point.
|
|
1864
2000
|
if (this.ws === null && this.currentState !== 'disconnected') {
|
|
1865
|
-
this.recoverFromFailedConnect(
|
|
2001
|
+
this.recoverFromFailedConnect(connectError);
|
|
1866
2002
|
}
|
|
1867
2003
|
});
|
|
1868
2004
|
}, delay);
|
|
@@ -1873,7 +2009,7 @@ class ConnectionManager {
|
|
|
1873
2009
|
// connect() already reported the refusal as the terminal error state.
|
|
1874
2010
|
return;
|
|
1875
2011
|
}
|
|
1876
|
-
this.handleReconnect(error
|
|
2012
|
+
this.handleReconnect(error);
|
|
1877
2013
|
}
|
|
1878
2014
|
clearReconnectTimeout() {
|
|
1879
2015
|
if (this.reconnectTimeout) {
|
|
@@ -8663,9 +8799,11 @@ function stringifyBigints(value) {
|
|
|
8663
8799
|
|
|
8664
8800
|
const Arete = Object.assign(Arete$1, { session: createSession });
|
|
8665
8801
|
|
|
8802
|
+
exports.API_PROBLEM_SCHEMA_VERSION = API_PROBLEM_SCHEMA_VERSION;
|
|
8666
8803
|
exports.ASSOCIATED_TOKEN_PROGRAM_ADDRESS = ASSOCIATED_TOKEN_PROGRAM_ADDRESS;
|
|
8667
8804
|
exports.Arete = Arete;
|
|
8668
8805
|
exports.AreteError = AreteError;
|
|
8806
|
+
exports.CLAIM_AGENT_MATERIALIZER_PATH = CLAIM_AGENT_MATERIALIZER_PATH;
|
|
8669
8807
|
exports.ConnectionManager = ConnectionManager;
|
|
8670
8808
|
exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
|
|
8671
8809
|
exports.DEFAULT_INITIAL_DATA_TIMEOUT_MS = DEFAULT_INITIAL_DATA_TIMEOUT_MS;
|
|
@@ -8751,10 +8889,12 @@ exports.getTransactionFailureOutcome = getTransactionFailureOutcome;
|
|
|
8751
8889
|
exports.hostedWebsocketSuffixes = hostedWebsocketSuffixes;
|
|
8752
8890
|
exports.inspectPreparedOperation = inspectPreparedOperation;
|
|
8753
8891
|
exports.instructionOperation = instructionOperation;
|
|
8892
|
+
exports.isClaimAgentRecoveryAction = isClaimAgentRecoveryAction;
|
|
8754
8893
|
exports.isEntityFrame = isEntityFrame;
|
|
8755
8894
|
exports.isErrorFrame = isErrorFrame;
|
|
8756
8895
|
exports.isHostedAreteEndpoint = isHostedAreteEndpoint;
|
|
8757
8896
|
exports.isReplayErrorCode = isReplayErrorCode;
|
|
8897
|
+
exports.isSafeClaimActionUrl = isSafeClaimActionUrl;
|
|
8758
8898
|
exports.isSameProgramSdk = isSameProgramSdk;
|
|
8759
8899
|
exports.isSnapshotFrame = isSnapshotFrame;
|
|
8760
8900
|
exports.isStackVersionRefusalCode = isStackVersionRefusalCode;
|
|
@@ -8766,6 +8906,7 @@ exports.normalizeSubscription = normalizeSubscription;
|
|
|
8766
8906
|
exports.normalizeSubscriptionQuery = normalizeSubscriptionQuery;
|
|
8767
8907
|
exports.normalizeSubscriptionRequest = normalizeSubscriptionRequest;
|
|
8768
8908
|
exports.normalizeTransactionError = normalizeTransactionError;
|
|
8909
|
+
exports.parseApiProblem = parseApiProblem;
|
|
8769
8910
|
exports.parseFrame = parseFrame;
|
|
8770
8911
|
exports.parseFrameFromBlob = parseFrameFromBlob;
|
|
8771
8912
|
exports.parseInstructionError = parseInstructionError;
|