@usearete/sdk 0.3.0 → 0.4.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 +58 -0
- package/dist/index.cjs +808 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +214 -20
- package/dist/index.esm.js +807 -80
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -510,6 +510,7 @@ const MIN_REFRESH_DELAY_MS = 1000;
|
|
|
510
510
|
const DEFAULT_QUERY_PARAMETER = 'hs_token';
|
|
511
511
|
const DEFAULT_HOSTED_TOKEN_ENDPOINT = 'https://api.arete.run/ws/sessions';
|
|
512
512
|
const HOSTED_WEBSOCKET_SUFFIX = '.stack.arete.run';
|
|
513
|
+
const MAX_HTTP_AUTH_TOKEN_STATES = 32;
|
|
513
514
|
function normalizeTokenResult(result) {
|
|
514
515
|
if (typeof result === 'string') {
|
|
515
516
|
return { token: result };
|
|
@@ -548,6 +549,71 @@ function parseJwtExpiry(token) {
|
|
|
548
549
|
function normalizeExpiryTimestamp(expiresAt, expires_at) {
|
|
549
550
|
return expiresAt ?? expires_at;
|
|
550
551
|
}
|
|
552
|
+
function normalizeScopes(scopes) {
|
|
553
|
+
return [...new Set(scopes)].sort();
|
|
554
|
+
}
|
|
555
|
+
function isProgramReadBindingAuthTarget(value) {
|
|
556
|
+
if (typeof value !== 'object' || value === null) {
|
|
557
|
+
return false;
|
|
558
|
+
}
|
|
559
|
+
const candidate = value;
|
|
560
|
+
return candidate['targetKind'] === 'program-read-binding'
|
|
561
|
+
&& typeof candidate['targetId'] === 'string'
|
|
562
|
+
&& typeof candidate['programReleaseHash'] === 'string';
|
|
563
|
+
}
|
|
564
|
+
function isAuthTokenTarget(value) {
|
|
565
|
+
if (isProgramReadBindingAuthTarget(value)) {
|
|
566
|
+
return true;
|
|
567
|
+
}
|
|
568
|
+
if (typeof value !== 'object' || value === null) {
|
|
569
|
+
return false;
|
|
570
|
+
}
|
|
571
|
+
const candidate = value;
|
|
572
|
+
return candidate['targetKind'] === 'solana-gateway-binding'
|
|
573
|
+
&& typeof candidate['targetId'] === 'string'
|
|
574
|
+
&& candidate['programReleaseHash'] === undefined;
|
|
575
|
+
}
|
|
576
|
+
function isAuthTokenRequest(value) {
|
|
577
|
+
return typeof value === 'object'
|
|
578
|
+
&& value !== null
|
|
579
|
+
&& Array.isArray(value.scopes);
|
|
580
|
+
}
|
|
581
|
+
function hasAuthTokenTarget(request) {
|
|
582
|
+
return isAuthTokenTarget(request);
|
|
583
|
+
}
|
|
584
|
+
function normalizeAuthTokenRequest(request) {
|
|
585
|
+
const scopes = normalizeScopes(request.scopes);
|
|
586
|
+
const hasTargetField = request.targetKind !== undefined
|
|
587
|
+
|| request.targetId !== undefined
|
|
588
|
+
|| request.programReleaseHash !== undefined;
|
|
589
|
+
if (!hasTargetField) {
|
|
590
|
+
return { scopes };
|
|
591
|
+
}
|
|
592
|
+
if (!hasAuthTokenTarget(request)) {
|
|
593
|
+
throw new AreteError('Targeted authentication requires a complete supported target identity', 'INVALID_CONFIG');
|
|
594
|
+
}
|
|
595
|
+
if (isProgramReadBindingAuthTarget(request)) {
|
|
596
|
+
return {
|
|
597
|
+
scopes,
|
|
598
|
+
targetKind: request.targetKind,
|
|
599
|
+
targetId: request.targetId,
|
|
600
|
+
programReleaseHash: request.programReleaseHash,
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
return {
|
|
604
|
+
scopes,
|
|
605
|
+
targetKind: request.targetKind,
|
|
606
|
+
targetId: request.targetId,
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
function createTargetedHttpTokenIdentity(request) {
|
|
610
|
+
return JSON.stringify([
|
|
611
|
+
request.targetKind,
|
|
612
|
+
request.targetId,
|
|
613
|
+
request.programReleaseHash ?? null,
|
|
614
|
+
normalizeScopes(request.scopes),
|
|
615
|
+
]);
|
|
616
|
+
}
|
|
551
617
|
function isRefreshAuthResponseMessage(value) {
|
|
552
618
|
if (typeof value !== 'object' || value === null) {
|
|
553
619
|
return false;
|
|
@@ -592,6 +658,7 @@ class ConnectionManager {
|
|
|
592
658
|
this.tokenRefreshTimeout = null;
|
|
593
659
|
this.tokenRefreshInFlight = null;
|
|
594
660
|
this.tokenRequestInFlight = null;
|
|
661
|
+
this.httpAuthTokenStates = new Map();
|
|
595
662
|
this.currentState = 'disconnected';
|
|
596
663
|
this.subscriptionQueue = new Map();
|
|
597
664
|
this.activeSubscriptions = new Map();
|
|
@@ -613,6 +680,12 @@ class ConnectionManager {
|
|
|
613
680
|
this.maxReconnectAttempts =
|
|
614
681
|
config.maxReconnectAttempts ?? DEFAULT_CONFIG.maxReconnectAttempts;
|
|
615
682
|
this.authConfig = config.auth;
|
|
683
|
+
this.authFetch = config.fetch ?? ((input, init) => {
|
|
684
|
+
if (typeof globalThis.fetch !== 'function') {
|
|
685
|
+
throw new AreteError('A fetch implementation is required for authentication token requests', 'INVALID_CONFIG');
|
|
686
|
+
}
|
|
687
|
+
return globalThis.fetch(input, init);
|
|
688
|
+
});
|
|
616
689
|
if (config.initialSubscriptions) {
|
|
617
690
|
for (const subscription of config.initialSubscriptions) {
|
|
618
691
|
this.addQueuedSubscription(subscription);
|
|
@@ -623,8 +696,10 @@ class ConnectionManager {
|
|
|
623
696
|
if (this.authConfig?.tokenEndpoint) {
|
|
624
697
|
return this.authConfig.tokenEndpoint;
|
|
625
698
|
}
|
|
626
|
-
//
|
|
627
|
-
|
|
699
|
+
// Hosted stacks always mint through the Arete Cloud token endpoint.
|
|
700
|
+
// Without a publishable key the session is minted anonymously — the API
|
|
701
|
+
// rate limits by IP/origin and issues low-trust public limits instead.
|
|
702
|
+
if (this.hostedAreteUrl) {
|
|
628
703
|
return DEFAULT_HOSTED_TOKEN_ENDPOINT;
|
|
629
704
|
}
|
|
630
705
|
return undefined;
|
|
@@ -669,9 +744,13 @@ class ConnectionManager {
|
|
|
669
744
|
return scopes.every((scope) => this.tokenScopes.has(scope));
|
|
670
745
|
}
|
|
671
746
|
async getOrRefreshToken(forceRefresh = false, requiredScopes = ['read']) {
|
|
672
|
-
|
|
747
|
+
const normalizedRequiredScopes = normalizeScopes(requiredScopes);
|
|
748
|
+
for (const scope of normalizedRequiredScopes)
|
|
673
749
|
this.requestedScopes.add(scope);
|
|
674
|
-
if (!forceRefresh
|
|
750
|
+
if (!forceRefresh
|
|
751
|
+
&& this.currentToken
|
|
752
|
+
&& !this.isTokenExpired()
|
|
753
|
+
&& this.tokenCovers(normalizedRequiredScopes)) {
|
|
675
754
|
return this.currentToken;
|
|
676
755
|
}
|
|
677
756
|
if (this.tokenRequestInFlight) {
|
|
@@ -679,18 +758,20 @@ class ConnectionManager {
|
|
|
679
758
|
if (!this.currentToken) {
|
|
680
759
|
return undefined;
|
|
681
760
|
}
|
|
682
|
-
if (this.currentToken
|
|
761
|
+
if (this.currentToken
|
|
762
|
+
&& !this.isTokenExpired()
|
|
763
|
+
&& this.tokenCovers(normalizedRequiredScopes)) {
|
|
683
764
|
return this.currentToken;
|
|
684
765
|
}
|
|
685
|
-
throw new AreteError(`Authentication token was not granted required scopes: ${
|
|
766
|
+
throw new AreteError(`Authentication token was not granted required scopes: ${normalizedRequiredScopes.join(', ')}`, 'AUTH_REQUIRED');
|
|
686
767
|
}
|
|
687
768
|
let request;
|
|
688
|
-
request = Promise.resolve().then(() => this.fetchAuthToken(
|
|
769
|
+
request = Promise.resolve().then(() => this.fetchAuthToken(normalizeScopes([...this.tokenScopes, ...this.requestedScopes]), () => this.tokenRequestInFlight === request));
|
|
689
770
|
this.tokenRequestInFlight = request;
|
|
690
771
|
try {
|
|
691
772
|
const token = await request;
|
|
692
|
-
if (token && !this.tokenCovers(
|
|
693
|
-
throw new AreteError(`Authentication token was not granted required scopes: ${
|
|
773
|
+
if (token && !this.tokenCovers(normalizedRequiredScopes)) {
|
|
774
|
+
throw new AreteError(`Authentication token was not granted required scopes: ${normalizedRequiredScopes.join(', ')}`, 'AUTH_REQUIRED');
|
|
694
775
|
}
|
|
695
776
|
return token;
|
|
696
777
|
}
|
|
@@ -701,27 +782,27 @@ class ConnectionManager {
|
|
|
701
782
|
}
|
|
702
783
|
}
|
|
703
784
|
async fetchAuthToken(scopes, isCurrent) {
|
|
785
|
+
const request = { scopes: normalizeScopes(scopes) };
|
|
786
|
+
const result = await this.requestAuthToken(request, isCurrent);
|
|
787
|
+
return result === undefined ? undefined : this.updateTokenState(result, request.scopes);
|
|
788
|
+
}
|
|
789
|
+
async requestAuthToken(request, isCurrent) {
|
|
704
790
|
const strategy = this.getAuthStrategy();
|
|
705
791
|
const requireCurrent = () => {
|
|
706
792
|
if (!isCurrent()) {
|
|
707
793
|
throw new AreteError('Authentication request was superseded', 'CONNECTION_CANCELLED');
|
|
708
794
|
}
|
|
709
795
|
};
|
|
710
|
-
// For hosted Arete URLs, auth is required - fail early with clear message
|
|
711
|
-
if (strategy.kind === 'none' && this.hostedAreteUrl) {
|
|
712
|
-
throw new AreteError('Arete authentication required. Please provide auth.publishableKey to AreteProvider. ' +
|
|
713
|
-
'Get your key from https://arete.run/dashboard', 'AUTH_REQUIRED');
|
|
714
|
-
}
|
|
715
796
|
switch (strategy.kind) {
|
|
716
797
|
case 'static-token': {
|
|
717
798
|
requireCurrent();
|
|
718
|
-
return
|
|
799
|
+
return strategy.token;
|
|
719
800
|
}
|
|
720
801
|
case 'token-provider':
|
|
721
802
|
try {
|
|
722
|
-
const result = await strategy.getToken(
|
|
803
|
+
const result = await strategy.getToken(request);
|
|
723
804
|
requireCurrent();
|
|
724
|
-
return
|
|
805
|
+
return result;
|
|
725
806
|
}
|
|
726
807
|
catch (error) {
|
|
727
808
|
if (error instanceof AreteError) {
|
|
@@ -731,9 +812,9 @@ class ConnectionManager {
|
|
|
731
812
|
}
|
|
732
813
|
case 'token-endpoint':
|
|
733
814
|
try {
|
|
734
|
-
const result = await this.fetchTokenFromEndpoint(strategy.endpoint,
|
|
815
|
+
const result = await this.fetchTokenFromEndpoint(strategy.endpoint, request);
|
|
735
816
|
requireCurrent();
|
|
736
|
-
return
|
|
817
|
+
return result;
|
|
737
818
|
}
|
|
738
819
|
catch (error) {
|
|
739
820
|
if (error instanceof AreteError) {
|
|
@@ -746,14 +827,97 @@ class ConnectionManager {
|
|
|
746
827
|
return undefined;
|
|
747
828
|
}
|
|
748
829
|
}
|
|
749
|
-
|
|
830
|
+
updateHttpTokenState(state, result, request) {
|
|
831
|
+
const normalized = normalizeTokenResult(result);
|
|
832
|
+
if (!normalized.token) {
|
|
833
|
+
throw new AreteError('Authentication provider returned an empty token', 'TOKEN_INVALID');
|
|
834
|
+
}
|
|
835
|
+
const grantedScopes = new Set(normalized.scopes ?? request.scopes);
|
|
836
|
+
if (!request.scopes.every((scope) => grantedScopes.has(scope))) {
|
|
837
|
+
throw new AreteError(`Authentication token was not granted required scopes: ${request.scopes.join(', ')}`, 'AUTH_REQUIRED');
|
|
838
|
+
}
|
|
839
|
+
const expiresAt = normalizeExpiryTimestamp(normalized.expiresAt, normalized.expires_at)
|
|
840
|
+
?? parseJwtExpiry(normalized.token);
|
|
841
|
+
if (this.isExpiryExpired(expiresAt)) {
|
|
842
|
+
throw new AreteError('Authentication token is expired', 'TOKEN_EXPIRED');
|
|
843
|
+
}
|
|
844
|
+
state.token = normalized.token;
|
|
845
|
+
state.expiresAt = expiresAt;
|
|
846
|
+
return normalized.token;
|
|
847
|
+
}
|
|
848
|
+
async getTargetedHttpAuthToken(request, forceRefresh) {
|
|
849
|
+
const identity = createTargetedHttpTokenIdentity(request);
|
|
850
|
+
let state = this.httpAuthTokenStates.get(identity);
|
|
851
|
+
if (!forceRefresh && state?.token && !this.isExpiryExpired(state.expiresAt)) {
|
|
852
|
+
this.touchHttpAuthTokenState(identity, state);
|
|
853
|
+
return state.token;
|
|
854
|
+
}
|
|
855
|
+
if (state?.inFlight) {
|
|
856
|
+
this.touchHttpAuthTokenState(identity, state);
|
|
857
|
+
return state.inFlight;
|
|
858
|
+
}
|
|
859
|
+
if (!state) {
|
|
860
|
+
state = {};
|
|
861
|
+
this.httpAuthTokenStates.set(identity, state);
|
|
862
|
+
}
|
|
863
|
+
const tokenState = state;
|
|
864
|
+
let inFlight;
|
|
865
|
+
inFlight = Promise.resolve().then(async () => {
|
|
866
|
+
const result = await this.requestAuthToken(request, () => this.httpAuthTokenStates.get(identity) === tokenState
|
|
867
|
+
&& tokenState.inFlight === inFlight);
|
|
868
|
+
return result === undefined
|
|
869
|
+
? undefined
|
|
870
|
+
: this.updateHttpTokenState(tokenState, result, request);
|
|
871
|
+
});
|
|
872
|
+
tokenState.inFlight = inFlight;
|
|
873
|
+
this.touchHttpAuthTokenState(identity, tokenState);
|
|
874
|
+
this.trimHttpAuthTokenStates();
|
|
875
|
+
try {
|
|
876
|
+
return await inFlight;
|
|
877
|
+
}
|
|
878
|
+
finally {
|
|
879
|
+
if (this.httpAuthTokenStates.get(identity) === tokenState
|
|
880
|
+
&& tokenState.inFlight === inFlight) {
|
|
881
|
+
tokenState.inFlight = undefined;
|
|
882
|
+
}
|
|
883
|
+
this.trimHttpAuthTokenStates();
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
touchHttpAuthTokenState(identity, state) {
|
|
887
|
+
this.httpAuthTokenStates.delete(identity);
|
|
888
|
+
this.httpAuthTokenStates.set(identity, state);
|
|
889
|
+
}
|
|
890
|
+
trimHttpAuthTokenStates() {
|
|
891
|
+
if (this.httpAuthTokenStates.size <= MAX_HTTP_AUTH_TOKEN_STATES) {
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
for (const [identity, state] of this.httpAuthTokenStates) {
|
|
895
|
+
if (!state.inFlight) {
|
|
896
|
+
this.httpAuthTokenStates.delete(identity);
|
|
897
|
+
}
|
|
898
|
+
if (this.httpAuthTokenStates.size <= MAX_HTTP_AUTH_TOKEN_STATES) {
|
|
899
|
+
break;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
createTokenEndpointRequestBody(request) {
|
|
904
|
+
if (hasAuthTokenTarget(request)) {
|
|
905
|
+
const target = {
|
|
906
|
+
targetKind: request.targetKind,
|
|
907
|
+
targetId: request.targetId,
|
|
908
|
+
scopes: request.scopes,
|
|
909
|
+
};
|
|
910
|
+
return isProgramReadBindingAuthTarget(request)
|
|
911
|
+
? { ...target, programReleaseHash: request.programReleaseHash }
|
|
912
|
+
: target;
|
|
913
|
+
}
|
|
750
914
|
return {
|
|
751
915
|
websocket_url: this.websocketUrl ?? '',
|
|
752
|
-
scopes,
|
|
916
|
+
scopes: request.scopes,
|
|
753
917
|
};
|
|
754
918
|
}
|
|
755
|
-
async fetchTokenFromEndpoint(tokenEndpoint,
|
|
756
|
-
const response = await
|
|
919
|
+
async fetchTokenFromEndpoint(tokenEndpoint, request) {
|
|
920
|
+
const response = await this.authFetch(tokenEndpoint, {
|
|
757
921
|
method: 'POST',
|
|
758
922
|
headers: {
|
|
759
923
|
...(this.authConfig?.publishableKey
|
|
@@ -763,7 +927,7 @@ class ConnectionManager {
|
|
|
763
927
|
'Content-Type': 'application/json',
|
|
764
928
|
},
|
|
765
929
|
credentials: this.authConfig?.tokenEndpointCredentials,
|
|
766
|
-
body: JSON.stringify(this.createTokenEndpointRequestBody(
|
|
930
|
+
body: JSON.stringify(this.createTokenEndpointRequestBody(request)),
|
|
767
931
|
});
|
|
768
932
|
if (!response.ok) {
|
|
769
933
|
const rawError = await response.text();
|
|
@@ -798,11 +962,14 @@ class ConnectionManager {
|
|
|
798
962
|
}
|
|
799
963
|
return data;
|
|
800
964
|
}
|
|
801
|
-
|
|
802
|
-
if (!
|
|
965
|
+
isExpiryExpired(expiresAt) {
|
|
966
|
+
if (!expiresAt) {
|
|
803
967
|
return false;
|
|
804
968
|
}
|
|
805
|
-
return Date.now() >= (
|
|
969
|
+
return Date.now() >= (expiresAt - TOKEN_REFRESH_BUFFER_SECONDS) * 1000;
|
|
970
|
+
}
|
|
971
|
+
isTokenExpired() {
|
|
972
|
+
return this.isExpiryExpired(this.tokenExpiry);
|
|
806
973
|
}
|
|
807
974
|
scheduleTokenRefresh() {
|
|
808
975
|
this.clearTokenRefreshTimeout();
|
|
@@ -950,17 +1117,75 @@ class ConnectionManager {
|
|
|
950
1117
|
getState() {
|
|
951
1118
|
return this.currentState;
|
|
952
1119
|
}
|
|
953
|
-
async getHttpAuthToken(
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1120
|
+
async getHttpAuthToken(requestOrScopesOrTargetOrForce = ['read'], scopesTargetOrForce = false, targetOrForce = false) {
|
|
1121
|
+
let request;
|
|
1122
|
+
let force = false;
|
|
1123
|
+
if (typeof requestOrScopesOrTargetOrForce === 'boolean') {
|
|
1124
|
+
request = { scopes: ['read'] };
|
|
1125
|
+
force = requestOrScopesOrTargetOrForce;
|
|
1126
|
+
}
|
|
1127
|
+
else if (Array.isArray(requestOrScopesOrTargetOrForce)) {
|
|
1128
|
+
if (isAuthTokenTarget(scopesTargetOrForce)) {
|
|
1129
|
+
request = {
|
|
1130
|
+
scopes: requestOrScopesOrTargetOrForce,
|
|
1131
|
+
...scopesTargetOrForce,
|
|
1132
|
+
};
|
|
1133
|
+
force = typeof targetOrForce === 'boolean' ? targetOrForce : false;
|
|
1134
|
+
}
|
|
1135
|
+
else if (isAuthTokenTarget(targetOrForce)) {
|
|
1136
|
+
request = {
|
|
1137
|
+
scopes: requestOrScopesOrTargetOrForce,
|
|
1138
|
+
...targetOrForce,
|
|
1139
|
+
};
|
|
1140
|
+
force = typeof scopesTargetOrForce === 'boolean' ? scopesTargetOrForce : false;
|
|
1141
|
+
}
|
|
1142
|
+
else {
|
|
1143
|
+
request = { scopes: requestOrScopesOrTargetOrForce };
|
|
1144
|
+
force = typeof scopesTargetOrForce === 'boolean' ? scopesTargetOrForce : false;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
else if (isAuthTokenRequest(requestOrScopesOrTargetOrForce)) {
|
|
1148
|
+
request = requestOrScopesOrTargetOrForce;
|
|
1149
|
+
force = typeof scopesTargetOrForce === 'boolean' ? scopesTargetOrForce : false;
|
|
1150
|
+
}
|
|
1151
|
+
else if (isAuthTokenTarget(requestOrScopesOrTargetOrForce)) {
|
|
1152
|
+
const requiredScopes = Array.isArray(scopesTargetOrForce)
|
|
1153
|
+
? scopesTargetOrForce
|
|
1154
|
+
: ['read'];
|
|
1155
|
+
request = {
|
|
1156
|
+
scopes: requiredScopes,
|
|
1157
|
+
...requestOrScopesOrTargetOrForce,
|
|
1158
|
+
};
|
|
1159
|
+
force = typeof scopesTargetOrForce === 'boolean'
|
|
1160
|
+
? scopesTargetOrForce
|
|
1161
|
+
: typeof targetOrForce === 'boolean' ? targetOrForce : false;
|
|
1162
|
+
}
|
|
1163
|
+
else {
|
|
1164
|
+
throw new AreteError('Invalid HTTP authentication token request', 'INVALID_CONFIG');
|
|
1165
|
+
}
|
|
1166
|
+
const normalizedRequest = normalizeAuthTokenRequest(request);
|
|
1167
|
+
return hasAuthTokenTarget(normalizedRequest)
|
|
1168
|
+
? this.getTargetedHttpAuthToken(normalizedRequest, force)
|
|
1169
|
+
: this.getOrRefreshToken(force, normalizedRequest.scopes);
|
|
961
1170
|
}
|
|
962
|
-
clearHttpAuthToken() {
|
|
963
|
-
|
|
1171
|
+
clearHttpAuthToken(identity, requiredScopes = ['read']) {
|
|
1172
|
+
if (identity === undefined) {
|
|
1173
|
+
this.clearTokenState();
|
|
1174
|
+
this.httpAuthTokenStates.clear();
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
const request = normalizeAuthTokenRequest(isAuthTokenRequest(identity)
|
|
1178
|
+
? identity
|
|
1179
|
+
: {
|
|
1180
|
+
scopes: requiredScopes,
|
|
1181
|
+
...identity,
|
|
1182
|
+
});
|
|
1183
|
+
if (hasAuthTokenTarget(request)) {
|
|
1184
|
+
this.httpAuthTokenStates.delete(createTargetedHttpTokenIdentity(request));
|
|
1185
|
+
}
|
|
1186
|
+
else {
|
|
1187
|
+
this.clearTokenState();
|
|
1188
|
+
}
|
|
964
1189
|
}
|
|
965
1190
|
onFrame(handler) {
|
|
966
1191
|
this.frameHandlers.add(handler);
|
|
@@ -2928,7 +3153,6 @@ async function parseReadResponse(response, path) {
|
|
|
2928
3153
|
function programAccountRead(input) {
|
|
2929
3154
|
return {
|
|
2930
3155
|
account: input.account,
|
|
2931
|
-
path: input.path,
|
|
2932
3156
|
schema: input.schema,
|
|
2933
3157
|
};
|
|
2934
3158
|
}
|
|
@@ -5027,7 +5251,7 @@ function mergeOperations(base, extension) {
|
|
|
5027
5251
|
function extendProgram(program, extensions) {
|
|
5028
5252
|
const base = program;
|
|
5029
5253
|
const extended = { ...program };
|
|
5030
|
-
delete extended.
|
|
5254
|
+
delete extended.sdkDefinitionHash;
|
|
5031
5255
|
for (const key of ['pdas', 'accounts', 'queries', 'addresses', 'constants', 'defaults', 'math']) {
|
|
5032
5256
|
const extensionValue = extensions[key];
|
|
5033
5257
|
if (extensionValue !== undefined) {
|
|
@@ -5592,6 +5816,131 @@ function formatPreparedOperation(operation) {
|
|
|
5592
5816
|
return lines.join('\n');
|
|
5593
5817
|
}
|
|
5594
5818
|
|
|
5819
|
+
function appendUrl(baseUrl, path) {
|
|
5820
|
+
return `${baseUrl.replace(/\/+$/, '')}/${path.replace(/^\/+/, '')}`;
|
|
5821
|
+
}
|
|
5822
|
+
function responseErrorCode(response, body) {
|
|
5823
|
+
const headerCode = response.headers.get('X-Error-Code');
|
|
5824
|
+
if (headerCode)
|
|
5825
|
+
return headerCode;
|
|
5826
|
+
try {
|
|
5827
|
+
const parsed = JSON.parse(body);
|
|
5828
|
+
if (typeof parsed.error?.code === 'string')
|
|
5829
|
+
return parsed.error.code;
|
|
5830
|
+
return typeof parsed.code === 'string' ? parsed.code : undefined;
|
|
5831
|
+
}
|
|
5832
|
+
catch {
|
|
5833
|
+
return undefined;
|
|
5834
|
+
}
|
|
5835
|
+
}
|
|
5836
|
+
function isRefreshableErrorCode(code) {
|
|
5837
|
+
return shouldRefreshToken(parseErrorCode(code.trim().replace(/_/g, '-')));
|
|
5838
|
+
}
|
|
5839
|
+
function parseProgramReadResponse(response, path, body) {
|
|
5840
|
+
if (!response.ok) {
|
|
5841
|
+
throw new ReadRequestError({
|
|
5842
|
+
status: response.status,
|
|
5843
|
+
path,
|
|
5844
|
+
body,
|
|
5845
|
+
serverErrorCode: responseErrorCode(response, body),
|
|
5846
|
+
});
|
|
5847
|
+
}
|
|
5848
|
+
try {
|
|
5849
|
+
return JSON.parse(body);
|
|
5850
|
+
}
|
|
5851
|
+
catch (error) {
|
|
5852
|
+
throw new AreteError(`Program read '${path}' returned invalid JSON`, 'INVALID_RESPONSE', error);
|
|
5853
|
+
}
|
|
5854
|
+
}
|
|
5855
|
+
function hostedAuthConfig(binding, runtimeAuth) {
|
|
5856
|
+
const metadata = binding.auth;
|
|
5857
|
+
const runtimeStrategyConfigured = runtimeAuth?.token !== undefined
|
|
5858
|
+
|| runtimeAuth?.getToken !== undefined
|
|
5859
|
+
|| runtimeAuth?.tokenEndpoint !== undefined;
|
|
5860
|
+
if (runtimeStrategyConfigured)
|
|
5861
|
+
return runtimeAuth;
|
|
5862
|
+
if (metadata.required === false)
|
|
5863
|
+
return runtimeAuth;
|
|
5864
|
+
return { ...runtimeAuth, tokenEndpoint: metadata.sessionEndpoint };
|
|
5865
|
+
}
|
|
5866
|
+
function hostedAuth(config) {
|
|
5867
|
+
const auth = hostedAuthConfig(config.binding, config.auth);
|
|
5868
|
+
if (auth?.token !== undefined && !auth.token) {
|
|
5869
|
+
throw new AreteError('Authentication token is empty', 'TOKEN_INVALID');
|
|
5870
|
+
}
|
|
5871
|
+
const bindingId = config.binding.programReadBindingId;
|
|
5872
|
+
if (!auth || auth.token !== undefined) {
|
|
5873
|
+
return { staticToken: auth?.token };
|
|
5874
|
+
}
|
|
5875
|
+
return {
|
|
5876
|
+
manager: new ConnectionManager({ websocketUrl: null, auth, fetch: config.fetch }),
|
|
5877
|
+
target: {
|
|
5878
|
+
targetKind: 'program-read-binding',
|
|
5879
|
+
targetId: bindingId,
|
|
5880
|
+
programReleaseHash: config.release.programReleaseHash,
|
|
5881
|
+
},
|
|
5882
|
+
};
|
|
5883
|
+
}
|
|
5884
|
+
function requestPath(release, request) {
|
|
5885
|
+
const releaseHash = encodeURIComponent(release.programReleaseHash).replace(/%3A/g, ':');
|
|
5886
|
+
const root = `/v1/releases/${releaseHash}`
|
|
5887
|
+
+ `/accounts/${encodeURIComponent(request.account)}`;
|
|
5888
|
+
if (request.operation === 'fetchMany')
|
|
5889
|
+
return root;
|
|
5890
|
+
const addressPath = `${root.replace(/\/+$/, '')}/${encodeURIComponent(request.address)}`;
|
|
5891
|
+
return request.operation === 'exists' ? `${addressPath}/exists` : addressPath;
|
|
5892
|
+
}
|
|
5893
|
+
function createProgramReadTransport(config) {
|
|
5894
|
+
if (config.kind === 'unavailable') {
|
|
5895
|
+
return {
|
|
5896
|
+
async read() {
|
|
5897
|
+
throw new AreteError(config.message, 'INVALID_CONFIG');
|
|
5898
|
+
},
|
|
5899
|
+
};
|
|
5900
|
+
}
|
|
5901
|
+
const auth = config.kind === 'hosted-binding' ? hostedAuth(config) : undefined;
|
|
5902
|
+
const endpoint = config.kind === 'hosted-binding'
|
|
5903
|
+
? config.binding.endpoint
|
|
5904
|
+
: config.endpoint;
|
|
5905
|
+
return {
|
|
5906
|
+
async read(request) {
|
|
5907
|
+
const path = requestPath(config.release, request);
|
|
5908
|
+
const input = appendUrl(endpoint, path);
|
|
5909
|
+
const attempt = async (forceRefresh) => {
|
|
5910
|
+
const token = auth?.staticToken
|
|
5911
|
+
?? (auth?.manager && auth.target
|
|
5912
|
+
? await auth.manager.getHttpAuthToken(auth.target, ['read'], forceRefresh)
|
|
5913
|
+
: undefined);
|
|
5914
|
+
const headers = new Headers(request.operation === 'fetchMany'
|
|
5915
|
+
? { 'content-type': 'application/json' }
|
|
5916
|
+
: undefined);
|
|
5917
|
+
if (token)
|
|
5918
|
+
headers.set('authorization', `Bearer ${token}`);
|
|
5919
|
+
return config.fetch(input, {
|
|
5920
|
+
method: request.operation === 'fetchMany' ? 'POST' : 'GET',
|
|
5921
|
+
headers,
|
|
5922
|
+
body: request.operation === 'fetchMany'
|
|
5923
|
+
? JSON.stringify({ addresses: request.addresses })
|
|
5924
|
+
: undefined,
|
|
5925
|
+
});
|
|
5926
|
+
};
|
|
5927
|
+
let response = await attempt(false);
|
|
5928
|
+
let body = await response.text();
|
|
5929
|
+
const wireCode = responseErrorCode(response, body);
|
|
5930
|
+
if (response.status === 401
|
|
5931
|
+
&& wireCode
|
|
5932
|
+
&& isRefreshableErrorCode(wireCode)
|
|
5933
|
+
&& auth?.manager
|
|
5934
|
+
&& auth.target) {
|
|
5935
|
+
auth.manager.clearHttpAuthToken(auth.target);
|
|
5936
|
+
response = await attempt(true);
|
|
5937
|
+
body = await response.text();
|
|
5938
|
+
}
|
|
5939
|
+
return parseProgramReadResponse(response, path, body);
|
|
5940
|
+
},
|
|
5941
|
+
};
|
|
5942
|
+
}
|
|
5943
|
+
|
|
5595
5944
|
class TransactionTransportError extends Error {
|
|
5596
5945
|
constructor(status, body) {
|
|
5597
5946
|
super(body.message);
|
|
@@ -5724,6 +6073,108 @@ function createTransactionTransport(baseUrl, authenticatedFetch) {
|
|
|
5724
6073
|
};
|
|
5725
6074
|
}
|
|
5726
6075
|
|
|
6076
|
+
function effectiveProgramRead(stack, name, override) {
|
|
6077
|
+
return override ?? stack.programReads?.[name];
|
|
6078
|
+
}
|
|
6079
|
+
function validateReleaseIdentity(programName, definition, release) {
|
|
6080
|
+
if (release
|
|
6081
|
+
&& definition.programSpecHash
|
|
6082
|
+
&& release.programSpecHash !== definition.programSpecHash) {
|
|
6083
|
+
throw new AreteError(`Program '${programName}' release programSpecHash '${release.programSpecHash}' does not match definition programSpecHash '${definition.programSpecHash}'`, 'PROGRAM_RELEASE_MISMATCH');
|
|
6084
|
+
}
|
|
6085
|
+
}
|
|
6086
|
+
function isNonEmptyString(value) {
|
|
6087
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
6088
|
+
}
|
|
6089
|
+
function isSecureOrLoopbackHttpUrl$1(value) {
|
|
6090
|
+
try {
|
|
6091
|
+
const url = new URL(value);
|
|
6092
|
+
return url.protocol === 'https:'
|
|
6093
|
+
|| (url.protocol === 'http:'
|
|
6094
|
+
&& ['localhost', '127.0.0.1', '::1'].includes(url.hostname));
|
|
6095
|
+
}
|
|
6096
|
+
catch {
|
|
6097
|
+
return false;
|
|
6098
|
+
}
|
|
6099
|
+
}
|
|
6100
|
+
function validateProgramReadDescriptor(programName, descriptor) {
|
|
6101
|
+
if (!descriptor.release
|
|
6102
|
+
|| !isNonEmptyString(descriptor.release.programReleaseHash)
|
|
6103
|
+
|| !isNonEmptyString(descriptor.release.programSpecHash)) {
|
|
6104
|
+
throw new AreteError(`Program '${programName}' read descriptor requires a complete release`, 'INVALID_CONFIG');
|
|
6105
|
+
}
|
|
6106
|
+
if (!descriptor.transport || typeof descriptor.transport !== 'object') {
|
|
6107
|
+
throw new AreteError(`Program '${programName}' read descriptor requires a transport`, 'INVALID_CONFIG');
|
|
6108
|
+
}
|
|
6109
|
+
if (descriptor.transport.kind === 'local-http') {
|
|
6110
|
+
if (descriptor.transport.endpointSource !== 'connect-http-url') {
|
|
6111
|
+
throw new AreteError(`Program '${programName}' local HTTP transport must use endpointSource 'connect-http-url'`, 'INVALID_CONFIG');
|
|
6112
|
+
}
|
|
6113
|
+
return;
|
|
6114
|
+
}
|
|
6115
|
+
if (descriptor.transport.kind !== 'hosted-binding') {
|
|
6116
|
+
throw new AreteError(`Program '${programName}' read descriptor has an unsupported transport`, 'INVALID_CONFIG');
|
|
6117
|
+
}
|
|
6118
|
+
const { binding } = descriptor.transport;
|
|
6119
|
+
if (!binding
|
|
6120
|
+
|| !isSecureOrLoopbackHttpUrl$1(binding.endpoint)
|
|
6121
|
+
|| !/^prb_[A-Za-z0-9_-]{32}$/.test(binding.programReadBindingId)
|
|
6122
|
+
|| !binding.auth
|
|
6123
|
+
|| binding.auth.targetKind !== 'program-read-binding'
|
|
6124
|
+
|| binding.auth.targetId !== binding.programReadBindingId
|
|
6125
|
+
|| !isSecureOrLoopbackHttpUrl$1(binding.auth.sessionEndpoint)) {
|
|
6126
|
+
throw new AreteError(`Program '${programName}' hosted binding requires secure endpoints, a canonical binding ID, and matching program-read-binding auth metadata`, 'INVALID_CONFIG');
|
|
6127
|
+
}
|
|
6128
|
+
}
|
|
6129
|
+
function validateProgramReads(stack, overrides, validateDescriptorKeys = true) {
|
|
6130
|
+
const programKeys = Object.keys(stack.programs ?? {});
|
|
6131
|
+
if (validateDescriptorKeys && stack.programReads) {
|
|
6132
|
+
const readKeys = Object.keys(stack.programReads);
|
|
6133
|
+
const missing = programKeys.filter((key) => !readKeys.includes(key));
|
|
6134
|
+
const extra = readKeys.filter((key) => !programKeys.includes(key));
|
|
6135
|
+
if (missing.length > 0 || extra.length > 0) {
|
|
6136
|
+
throw new AreteError(`Stack '${stack.name}' programReads keys must exactly match programs`
|
|
6137
|
+
+ `${missing.length > 0 ? `; missing: ${missing.join(', ')}` : ''}`
|
|
6138
|
+
+ `${extra.length > 0 ? `; unknown: ${extra.join(', ')}` : ''}`, 'INVALID_CONFIG');
|
|
6139
|
+
}
|
|
6140
|
+
}
|
|
6141
|
+
for (const [name, descriptor] of Object.entries(stack.programReads ?? {})) {
|
|
6142
|
+
validateProgramReadDescriptor(name, descriptor);
|
|
6143
|
+
}
|
|
6144
|
+
for (const key of Object.keys(overrides ?? {})) {
|
|
6145
|
+
if (!programKeys.includes(key)) {
|
|
6146
|
+
throw new AreteError(`Program read override '${key}' does not match a program in stack '${stack.name}'`, 'INVALID_CONFIG');
|
|
6147
|
+
}
|
|
6148
|
+
validateProgramReadDescriptor(key, overrides[key]);
|
|
6149
|
+
}
|
|
6150
|
+
for (const [name, definition] of Object.entries(stack.programs ?? {})) {
|
|
6151
|
+
validateReleaseIdentity(name, definition, stack.programReads?.[name]?.release);
|
|
6152
|
+
validateReleaseIdentity(name, definition, overrides?.[name]?.release);
|
|
6153
|
+
}
|
|
6154
|
+
}
|
|
6155
|
+
function hasCompleteIndependentProgramReads(stack, overrides, connectHttpUrl) {
|
|
6156
|
+
const programs = Object.entries(stack.programs ?? {});
|
|
6157
|
+
const readablePrograms = programs
|
|
6158
|
+
.filter(([, definition]) => hasProgramAccountReads(definition));
|
|
6159
|
+
return programs.length > 0 && readablePrograms.every(([name]) => {
|
|
6160
|
+
const read = effectiveProgramRead(stack, name, overrides?.[name]);
|
|
6161
|
+
return read?.transport.kind === 'hosted-binding'
|
|
6162
|
+
|| (read?.transport.kind === 'local-http' && isNonEmptyString(connectHttpUrl));
|
|
6163
|
+
});
|
|
6164
|
+
}
|
|
6165
|
+
function validateLocalProgramReadEndpoints(stack, overrides, connectHttpUrl) {
|
|
6166
|
+
for (const [name, definition] of Object.entries(stack.programs ?? {})) {
|
|
6167
|
+
if (!hasProgramAccountReads(definition))
|
|
6168
|
+
continue;
|
|
6169
|
+
const descriptor = effectiveProgramRead(stack, name, overrides?.[name]);
|
|
6170
|
+
if (descriptor?.transport.kind === 'local-http' && !isNonEmptyString(connectHttpUrl)) {
|
|
6171
|
+
throw new AreteError(`Program '${name}' local HTTP transport requires ConnectOptions.httpUrl`, 'INVALID_CONFIG');
|
|
6172
|
+
}
|
|
6173
|
+
}
|
|
6174
|
+
}
|
|
6175
|
+
function hasProgramAccountReads(definition) {
|
|
6176
|
+
return Object.keys(definition.accounts ?? {}).length > 0;
|
|
6177
|
+
}
|
|
5727
6178
|
function mergeAttachedPrograms(stack, attachedPrograms) {
|
|
5728
6179
|
const merged = { ...(attachedPrograms ?? {}) };
|
|
5729
6180
|
for (const [name, definition] of Object.entries(stack.programs ?? {})) {
|
|
@@ -5793,11 +6244,14 @@ function composeExecutionCallbacks(defaultCallback, callCallback) {
|
|
|
5793
6244
|
};
|
|
5794
6245
|
}
|
|
5795
6246
|
let Arete$1 = class Arete {
|
|
5796
|
-
constructor(url, httpBaseUrl, options) {
|
|
6247
|
+
constructor(url, httpBaseUrl, connectHttpUrl, programReadOverrides, options) {
|
|
5797
6248
|
this.stack = options.stack;
|
|
5798
6249
|
this._wallet = options.wallet;
|
|
5799
6250
|
this.executionDefaults = options.execution;
|
|
5800
6251
|
this.httpBaseUrl = httpBaseUrl;
|
|
6252
|
+
this.connectHttpUrl = connectHttpUrl;
|
|
6253
|
+
this.programReadOverrides = programReadOverrides;
|
|
6254
|
+
this.auth = options.auth;
|
|
5801
6255
|
this.fetchImpl = options.fetch ?? this.resolveFetchImpl();
|
|
5802
6256
|
this.storage = new SortedStorageDecorator(options.storage ?? new MemoryAdapter());
|
|
5803
6257
|
this.queryStore = new QueryStore(this.storage);
|
|
@@ -5816,6 +6270,7 @@ let Arete$1 = class Arete {
|
|
|
5816
6270
|
reconnectIntervals: options.reconnectIntervals,
|
|
5817
6271
|
maxReconnectAttempts: options.maxReconnectAttempts,
|
|
5818
6272
|
auth: options.auth,
|
|
6273
|
+
fetch: this.fetchImpl,
|
|
5819
6274
|
});
|
|
5820
6275
|
this.subscriptionRegistry = new SubscriptionRegistry(this.connection, this.queryStore);
|
|
5821
6276
|
this.connection.onFrame((frame) => {
|
|
@@ -5826,12 +6281,12 @@ let Arete$1 = class Arete {
|
|
|
5826
6281
|
});
|
|
5827
6282
|
this._views = createTypedViews(this.stack, this.storage, this.subscriptionRegistry);
|
|
5828
6283
|
this._queries = this.buildQueries();
|
|
5829
|
-
this._chain = createChainClient(this.httpBaseUrl, ((input, init) => this.
|
|
6284
|
+
this._chain = options.chain ?? createChainClient(this.httpBaseUrl ?? '', ((input, init) => this.authenticatedStackFetch(typeof input === 'string'
|
|
5830
6285
|
? input
|
|
5831
6286
|
: input instanceof URL
|
|
5832
6287
|
? input.toString()
|
|
5833
6288
|
: input.url, init, ['read'])));
|
|
5834
|
-
this._transactions = createTransactionTransport(this.httpBaseUrl, (input, init, scope, requirePreDispatchMarker) => this.
|
|
6289
|
+
this._transactions = options.transactions ?? createTransactionTransport(this.httpBaseUrl ?? '', (input, init, scope, requirePreDispatchMarker) => this.authenticatedStackFetch(input, init, [scope], requirePreDispatchMarker));
|
|
5835
6290
|
this._programs = this.buildPrograms();
|
|
5836
6291
|
}
|
|
5837
6292
|
resolveFetchImpl() {
|
|
@@ -5850,13 +6305,14 @@ let Arete$1 = class Arete {
|
|
|
5850
6305
|
buildPrograms() {
|
|
5851
6306
|
const bases = {};
|
|
5852
6307
|
for (const [name, definition] of Object.entries(this.stack.programs ?? {})) {
|
|
6308
|
+
const transport = this.createProgramTransport(name, definition);
|
|
5853
6309
|
const instructions = {};
|
|
5854
6310
|
for (const [instructionName, handler] of Object.entries(definition.rawInstructions ?? {})) {
|
|
5855
6311
|
instructions[instructionName] = this.createTypedInstruction(handler);
|
|
5856
6312
|
}
|
|
5857
6313
|
const accounts = {};
|
|
5858
6314
|
for (const [accountName, accountDefinition] of Object.entries(definition.accounts ?? {})) {
|
|
5859
|
-
accounts[accountName] = this.createAccountReader(accountDefinition);
|
|
6315
|
+
accounts[accountName] = this.createAccountReader(accountDefinition, transport);
|
|
5860
6316
|
}
|
|
5861
6317
|
const queries = {};
|
|
5862
6318
|
for (const [queryName, queryDefinition] of Object.entries(definition.queries ?? {})) {
|
|
@@ -5906,21 +6362,65 @@ let Arete$1 = class Arete {
|
|
|
5906
6362
|
build: (params, options) => buildInstruction(handler, params, this.withWallet(options)),
|
|
5907
6363
|
};
|
|
5908
6364
|
}
|
|
5909
|
-
|
|
6365
|
+
createProgramTransport(name, definition) {
|
|
6366
|
+
if (!hasProgramAccountReads(definition)) {
|
|
6367
|
+
return createProgramReadTransport({
|
|
6368
|
+
kind: 'unavailable',
|
|
6369
|
+
message: `Program '${name}' has no generated account readers`,
|
|
6370
|
+
});
|
|
6371
|
+
}
|
|
6372
|
+
const descriptor = effectiveProgramRead(this.stack, name, this.programReadOverrides[name]);
|
|
6373
|
+
validateReleaseIdentity(name, definition, descriptor?.release);
|
|
6374
|
+
if (descriptor?.transport.kind === 'local-http') {
|
|
6375
|
+
return createProgramReadTransport({
|
|
6376
|
+
kind: 'local-http',
|
|
6377
|
+
endpoint: this.connectHttpUrl,
|
|
6378
|
+
release: descriptor.release,
|
|
6379
|
+
fetch: this.fetchImpl,
|
|
6380
|
+
});
|
|
6381
|
+
}
|
|
6382
|
+
if (descriptor?.transport.kind === 'hosted-binding') {
|
|
6383
|
+
return createProgramReadTransport({
|
|
6384
|
+
kind: 'hosted-binding',
|
|
6385
|
+
release: descriptor.release,
|
|
6386
|
+
binding: descriptor.transport.binding,
|
|
6387
|
+
auth: this.auth,
|
|
6388
|
+
fetch: this.fetchImpl,
|
|
6389
|
+
});
|
|
6390
|
+
}
|
|
6391
|
+
return createProgramReadTransport({
|
|
6392
|
+
kind: 'unavailable',
|
|
6393
|
+
message: `Program '${name}' has no release-aware read descriptor`,
|
|
6394
|
+
});
|
|
6395
|
+
}
|
|
6396
|
+
createAccountReader(definition, transport) {
|
|
5910
6397
|
return {
|
|
5911
6398
|
fetch: async (address) => {
|
|
5912
|
-
const result = await
|
|
6399
|
+
const result = await transport.read({
|
|
6400
|
+
operation: 'fetch',
|
|
6401
|
+
account: definition.account,
|
|
6402
|
+
address,
|
|
6403
|
+
});
|
|
5913
6404
|
return result === null ? null : parseProgramAccountValue(definition, result);
|
|
5914
6405
|
},
|
|
5915
6406
|
fetchMany: async (addresses) => {
|
|
5916
|
-
const result = await
|
|
5917
|
-
|
|
5918
|
-
|
|
6407
|
+
const result = await transport.read({
|
|
6408
|
+
operation: 'fetchMany',
|
|
6409
|
+
account: definition.account,
|
|
6410
|
+
addresses,
|
|
5919
6411
|
});
|
|
5920
|
-
return
|
|
6412
|
+
return {
|
|
6413
|
+
items: result.items.map((item) => item.status === 'ok'
|
|
6414
|
+
? { ...item, value: parseProgramAccountValue(definition, item.value) }
|
|
6415
|
+
: item),
|
|
6416
|
+
};
|
|
5921
6417
|
},
|
|
5922
6418
|
exists: async (address) => {
|
|
5923
|
-
const result = await
|
|
6419
|
+
const result = await transport.read({
|
|
6420
|
+
operation: 'exists',
|
|
6421
|
+
account: definition.account,
|
|
6422
|
+
address,
|
|
6423
|
+
});
|
|
5924
6424
|
return result.exists;
|
|
5925
6425
|
},
|
|
5926
6426
|
};
|
|
@@ -5975,7 +6475,16 @@ let Arete$1 = class Arete {
|
|
|
5975
6475
|
}
|
|
5976
6476
|
return response;
|
|
5977
6477
|
}
|
|
6478
|
+
authenticatedStackFetch(input, init, requiredScopes = ['read'], requirePreDispatchMarker = false) {
|
|
6479
|
+
if (!this.httpBaseUrl) {
|
|
6480
|
+
throw new AreteError(`Stack '${this.stack.name}' has no HTTP endpoint; this operation is not a program account read`, 'INVALID_CONFIG');
|
|
6481
|
+
}
|
|
6482
|
+
return this.authenticatedFetch(input, init, requiredScopes, requirePreDispatchMarker);
|
|
6483
|
+
}
|
|
5978
6484
|
resolveReadUrl(path) {
|
|
6485
|
+
if (!this.httpBaseUrl) {
|
|
6486
|
+
throw new AreteError(`Stack '${this.stack.name}' has no HTTP endpoint; stack queries require httpUrl or endpoints.http`, 'INVALID_CONFIG');
|
|
6487
|
+
}
|
|
5979
6488
|
return `${this.httpBaseUrl.replace(/\/$/, '')}${path.startsWith('/') ? path : `/${path}`}`;
|
|
5980
6489
|
}
|
|
5981
6490
|
/** Merge the client's default wallet into call options (call options win). */
|
|
@@ -5987,21 +6496,35 @@ let Arete$1 = class Arete {
|
|
|
5987
6496
|
return merged;
|
|
5988
6497
|
}
|
|
5989
6498
|
static async connect(stack, options) {
|
|
6499
|
+
validateProgramReads(stack, undefined);
|
|
6500
|
+
const attachedPrograms = options?.programs;
|
|
6501
|
+
const effectiveStack = withPrograms(stack, attachedPrograms);
|
|
6502
|
+
const programReadOverrides = options?.programReads ?? {};
|
|
6503
|
+
validateProgramReads(effectiveStack, programReadOverrides, false);
|
|
5990
6504
|
const requestedUrl = options?.url ?? stack.endpoints.ws;
|
|
6505
|
+
const connectHttpUrl = options?.httpUrl;
|
|
6506
|
+
validateLocalProgramReadEndpoints(effectiveStack, programReadOverrides, connectHttpUrl);
|
|
5991
6507
|
const autoConnect = options?.autoConnect !== false;
|
|
5992
|
-
const
|
|
6508
|
+
const independentlyReadable = hasCompleteIndependentProgramReads(effectiveStack, programReadOverrides, connectHttpUrl);
|
|
6509
|
+
const implicitProgramOnlyHttp = options?.transport === undefined
|
|
6510
|
+
&& !requestedUrl
|
|
6511
|
+
&& independentlyReadable
|
|
6512
|
+
&& Object.keys(stack.views).length === 0;
|
|
6513
|
+
const httpOnly = options?.transport === 'http' || implicitProgramOnlyHttp;
|
|
5993
6514
|
const url = httpOnly ? null : requestedUrl;
|
|
5994
|
-
const
|
|
5995
|
-
|
|
5996
|
-
|
|
6515
|
+
const hasGeneratedHttp = Object.prototype.hasOwnProperty.call(stack.endpoints, 'http');
|
|
6516
|
+
let httpUrl = options?.httpUrl;
|
|
6517
|
+
if (httpUrl === undefined) {
|
|
6518
|
+
if (hasGeneratedHttp) {
|
|
6519
|
+
httpUrl = stack.endpoints.http;
|
|
6520
|
+
}
|
|
6521
|
+
}
|
|
5997
6522
|
if (!httpOnly && !url) {
|
|
5998
6523
|
throw new AreteError('WebSocket URL is required (provide url option or define endpoints.ws in stack)', 'INVALID_CONFIG');
|
|
5999
6524
|
}
|
|
6000
|
-
if (!httpUrl) {
|
|
6525
|
+
if (!httpUrl && !independentlyReadable) {
|
|
6001
6526
|
throw new AreteError('HTTP endpoint is required for transport: "http" (provide httpUrl option or define endpoints.http in stack)', 'INVALID_CONFIG');
|
|
6002
6527
|
}
|
|
6003
|
-
const attachedPrograms = options?.programs;
|
|
6004
|
-
const effectiveStack = withPrograms(stack, attachedPrograms);
|
|
6005
6528
|
const internalOptions = {
|
|
6006
6529
|
stack: effectiveStack,
|
|
6007
6530
|
httpUrl,
|
|
@@ -6018,8 +6541,10 @@ let Arete$1 = class Arete {
|
|
|
6018
6541
|
wallet: options?.wallet,
|
|
6019
6542
|
fetch: options?.fetch,
|
|
6020
6543
|
execution: options?.execution,
|
|
6544
|
+
chain: options?.chain,
|
|
6545
|
+
transactions: options?.transactions,
|
|
6021
6546
|
};
|
|
6022
|
-
const client = new Arete(url, httpUrl, internalOptions);
|
|
6547
|
+
const client = new Arete(url, httpUrl || undefined, connectHttpUrl, programReadOverrides, internalOptions);
|
|
6023
6548
|
if (!httpOnly && autoConnect) {
|
|
6024
6549
|
await client.connection.connect();
|
|
6025
6550
|
}
|
|
@@ -6230,12 +6755,22 @@ function createSignerRegistry(entries = []) {
|
|
|
6230
6755
|
};
|
|
6231
6756
|
}
|
|
6232
6757
|
|
|
6758
|
+
function mergeProgramReadOverrides(...layers) {
|
|
6759
|
+
return layers.reduce((selected, layer) => layer ?? selected, undefined);
|
|
6760
|
+
}
|
|
6761
|
+
function resolveSessionProgramReads(stack, member, options) {
|
|
6762
|
+
const reads = Object.fromEntries(Object.keys(stack.programs ?? {}).flatMap((name) => {
|
|
6763
|
+
const override = mergeProgramReadOverrides(options?.programRead, options?.programReads?.[name], member?.programRead, member?.programReads?.[name]);
|
|
6764
|
+
return override ? [[name, override]] : [];
|
|
6765
|
+
}));
|
|
6766
|
+
return Object.keys(reads).length > 0 ? reads : undefined;
|
|
6767
|
+
}
|
|
6233
6768
|
function resolveMemberConnectOptions(stack, member, options, forceHttpOnly) {
|
|
6234
6769
|
const transport = member?.transport ?? options?.transport ?? (forceHttpOnly ? 'http' : 'ws');
|
|
6235
|
-
//
|
|
6236
|
-
//
|
|
6770
|
+
// Stack endpoints remain on the stack definition. Only explicit session/member
|
|
6771
|
+
// HTTP options are forwarded as ConnectOptions.httpUrl for local Program Reads.
|
|
6237
6772
|
const url = member?.url ?? (stack.endpoints.ws || options?.endpoints?.ws);
|
|
6238
|
-
const httpUrl = member?.httpUrl ??
|
|
6773
|
+
const httpUrl = member?.httpUrl ?? options?.endpoints?.http;
|
|
6239
6774
|
return {
|
|
6240
6775
|
url,
|
|
6241
6776
|
httpUrl,
|
|
@@ -6246,33 +6781,87 @@ function resolveMemberConnectOptions(stack, member, options, forceHttpOnly) {
|
|
|
6246
6781
|
autoReconnect: member?.autoReconnect,
|
|
6247
6782
|
wallet: options?.wallet,
|
|
6248
6783
|
fetch: options?.fetch,
|
|
6784
|
+
programReads: resolveSessionProgramReads(stack, member, options),
|
|
6785
|
+
chain: options?.chain,
|
|
6786
|
+
transactions: options?.transactions,
|
|
6249
6787
|
};
|
|
6250
6788
|
}
|
|
6251
|
-
function
|
|
6789
|
+
function validateCompositionProgramReads(stack, member, options) {
|
|
6790
|
+
const overrides = resolveSessionProgramReads(stack, member, options);
|
|
6791
|
+
for (const [name, program] of Object.entries(stack.programs ?? {})) {
|
|
6792
|
+
const descriptor = overrides?.[name] ?? stack.programReads?.[name];
|
|
6793
|
+
if (descriptor)
|
|
6794
|
+
validateProgramReadDescriptor(name, descriptor);
|
|
6795
|
+
if (Object.keys(program.accounts ?? {}).length === 0)
|
|
6796
|
+
continue;
|
|
6797
|
+
const binding = descriptor?.transport.kind === 'hosted-binding'
|
|
6798
|
+
? descriptor.transport.binding
|
|
6799
|
+
: undefined;
|
|
6800
|
+
if (!descriptor?.release
|
|
6801
|
+
|| !binding
|
|
6802
|
+
|| !binding.endpoint?.trim()
|
|
6803
|
+
|| !binding.programReadBindingId?.trim()
|
|
6804
|
+
|| binding.auth?.targetKind !== 'program-read-binding'
|
|
6805
|
+
|| binding.auth.targetId !== binding.programReadBindingId
|
|
6806
|
+
|| !binding.auth.sessionEndpoint?.trim()) {
|
|
6807
|
+
throw new AreteError(`Composition session program '${name}' requires a complete hosted-binding descriptor or override`, 'INVALID_CONFIG');
|
|
6808
|
+
}
|
|
6809
|
+
}
|
|
6810
|
+
}
|
|
6811
|
+
function programAsStack(name, program, descriptor) {
|
|
6252
6812
|
return {
|
|
6253
6813
|
name,
|
|
6254
6814
|
endpoints: { ws: '' },
|
|
6255
6815
|
views: {},
|
|
6256
6816
|
programs: { [name]: program },
|
|
6817
|
+
...(descriptor ? { programReads: { [name]: descriptor } } : {}),
|
|
6257
6818
|
};
|
|
6258
6819
|
}
|
|
6259
|
-
async function createSession(definition,
|
|
6820
|
+
async function createSession(definition, ...args) {
|
|
6821
|
+
const options = args[0];
|
|
6260
6822
|
const stackEntries = Object.entries(definition.stacks ?? {});
|
|
6261
6823
|
const programEntries = Object.entries(definition.programs ?? {});
|
|
6262
6824
|
if (stackEntries.length === 0 && programEntries.length === 0) {
|
|
6263
6825
|
throw new AreteError('createSession requires at least one stack or program member', 'INVALID_CONFIG');
|
|
6264
6826
|
}
|
|
6827
|
+
if (definition.mode === 'composition' && (!options?.chain || !options?.transactions)) {
|
|
6828
|
+
throw new AreteError('composition sessions require explicit chain and transaction transports', 'INVALID_CONFIG');
|
|
6829
|
+
}
|
|
6830
|
+
if (definition.mode === 'composition' && options?.endpoints !== undefined) {
|
|
6831
|
+
throw new AreteError('composition sessions require per-member live endpoints, not shared fallback endpoints', 'INVALID_CONFIG');
|
|
6832
|
+
}
|
|
6833
|
+
if (definition.programReads) {
|
|
6834
|
+
const programKeys = programEntries.map(([key]) => key);
|
|
6835
|
+
const descriptorKeys = Object.keys(definition.programReads);
|
|
6836
|
+
if (programKeys.some((key) => !descriptorKeys.includes(key))
|
|
6837
|
+
|| descriptorKeys.some((key) => !programKeys.includes(key))) {
|
|
6838
|
+
throw new AreteError('Session definition programReads keys must exactly match standalone programs', 'INVALID_CONFIG');
|
|
6839
|
+
}
|
|
6840
|
+
}
|
|
6841
|
+
if (definition.mode === 'composition') {
|
|
6842
|
+
for (const [key, stack] of stackEntries) {
|
|
6843
|
+
const member = options?.stacks?.[key];
|
|
6844
|
+
validateCompositionProgramReads(withPrograms(stack, member?.programs), member, options);
|
|
6845
|
+
}
|
|
6846
|
+
for (const [key, program] of programEntries) {
|
|
6847
|
+
validateCompositionProgramReads(programAsStack(key, program, definition.programReads?.[key]), options?.programs?.[key], options);
|
|
6848
|
+
}
|
|
6849
|
+
}
|
|
6265
6850
|
let wallet = options?.wallet;
|
|
6266
6851
|
const signerRegistry = options?.signerRegistry ?? createSignerRegistry();
|
|
6267
6852
|
const connectedStacks = await Promise.all(stackEntries.map(async ([key, stack]) => {
|
|
6268
6853
|
const memberOptions = options?.stacks?.[key];
|
|
6269
6854
|
const effectiveStack = withPrograms(stack, memberOptions?.programs);
|
|
6270
6855
|
const connectOptions = resolveMemberConnectOptions(effectiveStack, memberOptions, options, false);
|
|
6271
|
-
const client = await Arete$1.connect(
|
|
6856
|
+
const client = await Arete$1.connect(stack, {
|
|
6857
|
+
...connectOptions,
|
|
6858
|
+
programs: memberOptions?.programs,
|
|
6859
|
+
programReads: connectOptions.programReads,
|
|
6860
|
+
});
|
|
6272
6861
|
return [key, client];
|
|
6273
6862
|
}));
|
|
6274
6863
|
const connectedPrograms = await Promise.all(programEntries.map(async ([key, program]) => {
|
|
6275
|
-
const syntheticStack = programAsStack(key, program);
|
|
6864
|
+
const syntheticStack = programAsStack(key, program, definition.programReads?.[key]);
|
|
6276
6865
|
const connectOptions = resolveMemberConnectOptions(syntheticStack, options?.programs?.[key], options, true);
|
|
6277
6866
|
const client = await Arete$1.connect(syntheticStack, connectOptions);
|
|
6278
6867
|
return [key, client];
|
|
@@ -6291,21 +6880,23 @@ async function createSession(definition, options) {
|
|
|
6291
6880
|
client.programs[key],
|
|
6292
6881
|
]);
|
|
6293
6882
|
const promotedPrograms = Object.fromEntries(connectedProgramEntries);
|
|
6294
|
-
|
|
6295
|
-
for (const [
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
`
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6883
|
+
if (definition.mode !== 'composition') {
|
|
6884
|
+
for (const [stackKey, client] of connectedStacks) {
|
|
6885
|
+
for (const [programKey, program] of Object.entries(client.programs)) {
|
|
6886
|
+
if (explicitProgramKeys.has(programKey)) {
|
|
6887
|
+
continue;
|
|
6888
|
+
}
|
|
6889
|
+
const existingOwner = programOwners.get(programKey);
|
|
6890
|
+
if (existingOwner) {
|
|
6891
|
+
console.warn(`Program '${programKey}' is bundled by stacks '${existingOwner.stack}'` +
|
|
6892
|
+
` (${existingOwner.programId ?? 'unknown program ID'}) and '${stackKey}'` +
|
|
6893
|
+
` (${program.programId ?? 'unknown program ID'}); session.programs.${programKey}` +
|
|
6894
|
+
` uses '${existingOwner.stack}' because it was connected first`);
|
|
6895
|
+
continue;
|
|
6896
|
+
}
|
|
6897
|
+
promotedPrograms[programKey] = program;
|
|
6898
|
+
programOwners.set(programKey, { stack: stackKey, programId: program.programId });
|
|
6306
6899
|
}
|
|
6307
|
-
promotedPrograms[programKey] = program;
|
|
6308
|
-
programOwners.set(programKey, { stack: stackKey, programId: program.programId });
|
|
6309
6900
|
}
|
|
6310
6901
|
}
|
|
6311
6902
|
const programs = promotedPrograms;
|
|
@@ -6386,6 +6977,142 @@ function flowOperation(prepare) {
|
|
|
6386
6977
|
};
|
|
6387
6978
|
}
|
|
6388
6979
|
|
|
6980
|
+
function isSecureOrLoopbackHttpUrl(value) {
|
|
6981
|
+
try {
|
|
6982
|
+
const url = new URL(value);
|
|
6983
|
+
return url.protocol === 'https:'
|
|
6984
|
+
|| (url.protocol === 'http:'
|
|
6985
|
+
&& ['localhost', '127.0.0.1', '::1'].includes(url.hostname));
|
|
6986
|
+
}
|
|
6987
|
+
catch {
|
|
6988
|
+
return false;
|
|
6989
|
+
}
|
|
6990
|
+
}
|
|
6991
|
+
function validateBinding(capability, binding, requiredScopes) {
|
|
6992
|
+
const auth = binding?.auth;
|
|
6993
|
+
const complete = binding
|
|
6994
|
+
&& isSecureOrLoopbackHttpUrl(binding.endpoint)
|
|
6995
|
+
&& /^sgb_[A-Za-z0-9_-]{32}$/.test(binding.solanaGatewayBindingId)
|
|
6996
|
+
&& binding.cluster.trim().length > 0
|
|
6997
|
+
&& binding.region.trim().length > 0
|
|
6998
|
+
&& auth
|
|
6999
|
+
&& typeof auth.required === 'boolean'
|
|
7000
|
+
&& auth.mode === binding.authPolicy
|
|
7001
|
+
&& isSecureOrLoopbackHttpUrl(auth.sessionEndpoint)
|
|
7002
|
+
&& isSecureOrLoopbackHttpUrl(auth.jwksUrl)
|
|
7003
|
+
&& auth.tokenTransport === 'bearer'
|
|
7004
|
+
&& auth.audience === 'arete:solana-gateway'
|
|
7005
|
+
&& auth.targetKind === 'solana-gateway-binding'
|
|
7006
|
+
&& auth.targetId === binding.solanaGatewayBindingId
|
|
7007
|
+
&& Array.isArray(auth.scopes)
|
|
7008
|
+
&& requiredScopes.every((scope) => auth.scopes.includes(scope))
|
|
7009
|
+
&& Array.isArray(auth.acceptedKeyClasses)
|
|
7010
|
+
&& typeof auth.transactionEntitlementRequired === 'boolean';
|
|
7011
|
+
if (!complete) {
|
|
7012
|
+
throw new AreteError(`Hosted Solana gateway ${capability} binding is incomplete or inconsistent`, 'INVALID_CONFIG');
|
|
7013
|
+
}
|
|
7014
|
+
}
|
|
7015
|
+
function resolveFetch(fetchImpl) {
|
|
7016
|
+
if (fetchImpl)
|
|
7017
|
+
return fetchImpl;
|
|
7018
|
+
if (typeof globalThis.fetch === 'function')
|
|
7019
|
+
return globalThis.fetch.bind(globalThis);
|
|
7020
|
+
throw new AreteError('A fetch implementation is required for hosted Solana gateway transports', 'INVALID_CONFIG');
|
|
7021
|
+
}
|
|
7022
|
+
function hasRuntimeAuthStrategy(auth) {
|
|
7023
|
+
return auth?.token !== undefined
|
|
7024
|
+
|| auth?.getToken !== undefined
|
|
7025
|
+
|| auth?.tokenEndpoint !== undefined;
|
|
7026
|
+
}
|
|
7027
|
+
function bindingAuthConfig(binding, runtimeAuth) {
|
|
7028
|
+
if (hasRuntimeAuthStrategy(runtimeAuth))
|
|
7029
|
+
return runtimeAuth;
|
|
7030
|
+
if (!binding.auth.required)
|
|
7031
|
+
return runtimeAuth;
|
|
7032
|
+
return { ...runtimeAuth, tokenEndpoint: binding.auth.sessionEndpoint };
|
|
7033
|
+
}
|
|
7034
|
+
function refreshableErrorCode(response) {
|
|
7035
|
+
const wireCode = response.headers.get('X-Error-Code');
|
|
7036
|
+
return wireCode !== null
|
|
7037
|
+
&& shouldRefreshToken(parseErrorCode(wireCode.trim().replace(/_/g, '-')));
|
|
7038
|
+
}
|
|
7039
|
+
/**
|
|
7040
|
+
* Construct explicit hosted chain and transaction transports from generated
|
|
7041
|
+
* gateway descriptors. Tokens are isolated by exact binding target and scope.
|
|
7042
|
+
*/
|
|
7043
|
+
function createHostedSolanaGatewayTransports(bindings, options = {}) {
|
|
7044
|
+
validateBinding('chain', bindings.chain, ['read']);
|
|
7045
|
+
validateBinding('transactions', bindings.transactions, ['transaction:inspect', 'transaction:send']);
|
|
7046
|
+
const fetchImpl = resolveFetch(options.fetch);
|
|
7047
|
+
const managers = new Map();
|
|
7048
|
+
const bindingAuthentication = (binding) => {
|
|
7049
|
+
const auth = bindingAuthConfig(binding, options.auth);
|
|
7050
|
+
if (auth?.token !== undefined) {
|
|
7051
|
+
if (!auth.token) {
|
|
7052
|
+
throw new AreteError('Authentication token is empty', 'TOKEN_INVALID');
|
|
7053
|
+
}
|
|
7054
|
+
return {
|
|
7055
|
+
staticToken: auth.token,
|
|
7056
|
+
target: {
|
|
7057
|
+
targetKind: 'solana-gateway-binding',
|
|
7058
|
+
targetId: binding.solanaGatewayBindingId,
|
|
7059
|
+
},
|
|
7060
|
+
};
|
|
7061
|
+
}
|
|
7062
|
+
let manager;
|
|
7063
|
+
if (auth && (auth.getToken || auth.tokenEndpoint)) {
|
|
7064
|
+
const identity = hasRuntimeAuthStrategy(options.auth)
|
|
7065
|
+
? 'runtime-auth-strategy'
|
|
7066
|
+
: `session-endpoint:${binding.auth.sessionEndpoint}`;
|
|
7067
|
+
manager = managers.get(identity);
|
|
7068
|
+
if (!manager) {
|
|
7069
|
+
manager = new ConnectionManager({ websocketUrl: null, auth, fetch: fetchImpl });
|
|
7070
|
+
managers.set(identity, manager);
|
|
7071
|
+
}
|
|
7072
|
+
}
|
|
7073
|
+
return {
|
|
7074
|
+
manager,
|
|
7075
|
+
target: {
|
|
7076
|
+
targetKind: 'solana-gateway-binding',
|
|
7077
|
+
targetId: binding.solanaGatewayBindingId,
|
|
7078
|
+
},
|
|
7079
|
+
};
|
|
7080
|
+
};
|
|
7081
|
+
const authenticatedFetch = (authentication) => async (input, init, scope, requirePreDispatchMarker) => {
|
|
7082
|
+
const request = { ...authentication.target, scopes: [scope] };
|
|
7083
|
+
const attempt = async (forceRefresh) => {
|
|
7084
|
+
const token = authentication.staticToken
|
|
7085
|
+
?? (authentication.manager
|
|
7086
|
+
? await authentication.manager.getHttpAuthToken(request, forceRefresh)
|
|
7087
|
+
: undefined);
|
|
7088
|
+
const headers = new Headers(init?.headers);
|
|
7089
|
+
if (token)
|
|
7090
|
+
headers.set('authorization', `Bearer ${token}`);
|
|
7091
|
+
return fetchImpl(input, { ...init, headers });
|
|
7092
|
+
};
|
|
7093
|
+
let response = await attempt(false);
|
|
7094
|
+
const explicitlyNotDispatched = response.headers.get('X-Arete-Upstream-Attempted') === 'false';
|
|
7095
|
+
if (!response.ok
|
|
7096
|
+
&& authentication.manager
|
|
7097
|
+
&& refreshableErrorCode(response)
|
|
7098
|
+
&& (!requirePreDispatchMarker || explicitlyNotDispatched)) {
|
|
7099
|
+
authentication.manager.clearHttpAuthToken(request);
|
|
7100
|
+
response = await attempt(true);
|
|
7101
|
+
}
|
|
7102
|
+
return response;
|
|
7103
|
+
};
|
|
7104
|
+
const chainAuthentication = bindingAuthentication(bindings.chain);
|
|
7105
|
+
const transactionAuthentication = bindingAuthentication(bindings.transactions);
|
|
7106
|
+
const chainFetch = authenticatedFetch(chainAuthentication);
|
|
7107
|
+
const transactionFetch = authenticatedFetch(transactionAuthentication);
|
|
7108
|
+
return {
|
|
7109
|
+
chain: createChainClient(bindings.chain.endpoint, ((input, init) => chainFetch(typeof input === 'string'
|
|
7110
|
+
? input
|
|
7111
|
+
: input instanceof URL ? input.toString() : input.url, init, 'read', false))),
|
|
7112
|
+
transactions: createTransactionTransport(bindings.transactions.endpoint, (input, init, scope, requirePreDispatchMarker) => transactionFetch(input, init, scope, requirePreDispatchMarker)),
|
|
7113
|
+
};
|
|
7114
|
+
}
|
|
7115
|
+
|
|
6389
7116
|
function chainAccountLoader(chain) {
|
|
6390
7117
|
return {
|
|
6391
7118
|
async getAccount(address) {
|
|
@@ -6545,5 +7272,5 @@ function stringifyBigints(value) {
|
|
|
6545
7272
|
|
|
6546
7273
|
const Arete = Object.assign(Arete$1, { session: createSession });
|
|
6547
7274
|
|
|
6548
|
-
export { ASSOCIATED_TOKEN_PROGRAM_ADDRESS, Arete, AreteError, ConnectionManager, DEFAULT_CONFIG, DEFAULT_MAX_ENTRIES_PER_VIEW, FrameProcessor, InstructionError, MemoryAdapter, OperationCallbackError, OperationExecutionError, PROGRAM_OPERATION_EXTENSIONS, ProcessedSlotTimeoutError, QueryStore, ReadRequestError, SPL_TOKEN_PROGRAM_ADDRESS, STACK_RUNTIME_EXTENSIONS, SYSTEM_PROGRAM_ADDRESS, SubscriptionRegistry, TOKEN_2022_PROGRAM_ADDRESS, TransactionExecutionError, TransactionTransportError, account, appendFlowTransactions, appendTransactionInstructions, applyConnectedStackExtensions, arg, buildInstruction, bytes, canonicalQueryKey, chainAccountLoader, createChainClient, createEntityStream, createInstructionExecutor, createInstructionHandler, createPreparedFlow, createPreparedInstruction, createPreparedTransaction, createPreparedTransactionBody, createProgramPdas, createPublicKeySeed, createRichUpdateStream, createSeed, createSession, createSignerRegistry, createSubscriptionId, createTransactionTransport, createTypedListView, createTypedStateView, createTypedViews, createUpdateStream, decodeBase58, defineProgramExtensions, defineStackExtensions, deriveAssociatedTokenAccount, deriveHttpEndpoint, findProgramAddress as derivePda, describePreparedOperation, encodeBase58, executeInstruction, executePreparedOperation, extendProgram, extendPrograms, extendStack, findProgramAddress, findProgramAddressSync, flowOperation, formatPreparedOperation, formatProgramError, formatRawToUi, getMintDecimals, getProgramRuntimeExtensions, getStackRuntimeExtensions, getTransactionFailureOutcome, inspectPreparedOperation, instructionOperation, isEntityFrame, isErrorFrame, isHostedAreteEndpoint, isSnapshotFrame, isSubscribedFrame, isUnsubscribedFrame, isValidFrame, literal, normalizeSubscription, normalizeSubscriptionQuery, normalizeSubscriptionRequest, normalizeTransactionError, parseFrame, parseFrameFromBlob, parseInstructionError, parseUiAmountToRaw, pda, prependFlowTransactionInstructions, prependTransactionInstructions, programAccountRead, programQuery, resolveAccounts, resolveAmount, resolveAmountToRaw, resolveAmountsToRaw, resolveTokenProgramAddress, safeToRawAmount, serializeInstructionData, serializeViewKey, stackQuery, stringifyBigints, toJsonValue, toRawAmount, transactionOperation, unwrapOperationExecutionError, validateAccountResolution, validateSubscriptionId, withPrograms };
|
|
7275
|
+
export { ASSOCIATED_TOKEN_PROGRAM_ADDRESS, Arete, AreteError, ConnectionManager, DEFAULT_CONFIG, DEFAULT_MAX_ENTRIES_PER_VIEW, FrameProcessor, InstructionError, MemoryAdapter, OperationCallbackError, OperationExecutionError, PROGRAM_OPERATION_EXTENSIONS, ProcessedSlotTimeoutError, QueryStore, ReadRequestError, SPL_TOKEN_PROGRAM_ADDRESS, STACK_RUNTIME_EXTENSIONS, SYSTEM_PROGRAM_ADDRESS, SubscriptionRegistry, TOKEN_2022_PROGRAM_ADDRESS, TransactionExecutionError, TransactionTransportError, account, appendFlowTransactions, appendTransactionInstructions, applyConnectedStackExtensions, arg, buildInstruction, bytes, canonicalQueryKey, chainAccountLoader, createChainClient, createEntityStream, createHostedSolanaGatewayTransports, createInstructionExecutor, createInstructionHandler, createPreparedFlow, createPreparedInstruction, createPreparedTransaction, createPreparedTransactionBody, createProgramPdas, createPublicKeySeed, createRichUpdateStream, createSeed, createSession, createSignerRegistry, createSubscriptionId, createTransactionTransport, createTypedListView, createTypedStateView, createTypedViews, createUpdateStream, decodeBase58, defineProgramExtensions, defineStackExtensions, deriveAssociatedTokenAccount, deriveHttpEndpoint, findProgramAddress as derivePda, describePreparedOperation, encodeBase58, executeInstruction, executePreparedOperation, extendProgram, extendPrograms, extendStack, findProgramAddress, findProgramAddressSync, flowOperation, formatPreparedOperation, formatProgramError, formatRawToUi, getMintDecimals, getProgramRuntimeExtensions, getStackRuntimeExtensions, getTransactionFailureOutcome, inspectPreparedOperation, instructionOperation, isEntityFrame, isErrorFrame, isHostedAreteEndpoint, isSnapshotFrame, isSubscribedFrame, isUnsubscribedFrame, isValidFrame, literal, normalizeSubscription, normalizeSubscriptionQuery, normalizeSubscriptionRequest, normalizeTransactionError, parseFrame, parseFrameFromBlob, parseInstructionError, parseUiAmountToRaw, pda, prependFlowTransactionInstructions, prependTransactionInstructions, programAccountRead, programQuery, resolveAccounts, resolveAmount, resolveAmountToRaw, resolveAmountsToRaw, resolveTokenProgramAddress, safeToRawAmount, serializeInstructionData, serializeViewKey, stackQuery, stringifyBigints, toJsonValue, toRawAmount, transactionOperation, unwrapOperationExecutionError, validateAccountResolution, validateProgramReadDescriptor, validateSubscriptionId, withPrograms };
|
|
6549
7276
|
//# sourceMappingURL=index.esm.js.map
|