@tigeropenapi/tigeropen 0.5.5 → 0.5.7
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 +27 -0
- package/dist/cjs/auth/authentication.js +74 -0
- package/dist/cjs/auth/index.js +24 -0
- package/dist/cjs/auth/oauth2/client-registration.js +44 -0
- package/dist/cjs/auth/oauth2/device-authorization.js +40 -0
- package/dist/cjs/auth/oauth2/errors.js +64 -0
- package/dist/cjs/auth/oauth2/http-utils.js +112 -0
- package/dist/cjs/auth/oauth2/index.js +42 -0
- package/dist/cjs/auth/oauth2/loopback-receiver.js +215 -0
- package/dist/cjs/auth/oauth2/metadata.js +34 -0
- package/dist/cjs/auth/oauth2/oauth2-authentication.js +74 -0
- package/dist/cjs/auth/oauth2/session-manager.js +762 -0
- package/dist/cjs/auth/oauth2/token-store.js +252 -0
- package/dist/cjs/auth/oauth2/token.js +76 -0
- package/dist/cjs/push/pb/Request.js +12 -1
- package/dist/cjs/push/proto-message.js +5 -1
- package/dist/cjs/push/push-client.js +29 -13
- package/dist/cjs/trade/trade-client.js +12 -3
- package/dist/cjs/version.js +1 -1
- package/dist/esm/auth/authentication.js +68 -0
- package/dist/esm/auth/index.js +3 -0
- package/dist/esm/auth/oauth2/client-registration.js +40 -0
- package/dist/esm/auth/oauth2/device-authorization.js +36 -0
- package/dist/esm/auth/oauth2/errors.js +59 -0
- package/dist/esm/auth/oauth2/http-utils.js +105 -0
- package/dist/esm/auth/oauth2/index.js +11 -0
- package/dist/esm/auth/oauth2/loopback-receiver.js +178 -0
- package/dist/esm/auth/oauth2/metadata.js +30 -0
- package/dist/esm/auth/oauth2/oauth2-authentication.js +70 -0
- package/dist/esm/auth/oauth2/session-manager.js +725 -0
- package/dist/esm/auth/oauth2/token-store.js +214 -0
- package/dist/esm/auth/oauth2/token.js +71 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/push/pb/Request.js +12 -1
- package/dist/esm/push/proto-message.js +5 -1
- package/dist/esm/push/push-client.js +29 -13
- package/dist/esm/trade/trade-client.js +12 -3
- package/dist/esm/version.js +1 -1
- package/dist/types/auth/authentication.d.ts +98 -0
- package/dist/types/auth/index.d.ts +4 -0
- package/dist/types/auth/oauth2/client-registration.d.ts +32 -0
- package/dist/types/auth/oauth2/device-authorization.d.ts +40 -0
- package/dist/types/auth/oauth2/errors.d.ts +46 -0
- package/dist/types/auth/oauth2/http-utils.d.ts +21 -0
- package/dist/types/auth/oauth2/index.d.ts +16 -0
- package/dist/types/auth/oauth2/loopback-receiver.d.ts +29 -0
- package/dist/types/auth/oauth2/metadata.d.ts +32 -0
- package/dist/types/auth/oauth2/oauth2-authentication.d.ts +31 -0
- package/dist/types/auth/oauth2/session-manager.d.ts +241 -0
- package/dist/types/auth/oauth2/token-store.d.ts +49 -0
- package/dist/types/auth/oauth2/token.d.ts +56 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/model/quote.d.ts +1 -0
- package/dist/types/model/trade-requests.d.ts +5 -0
- package/dist/types/push/index.d.ts +1 -1
- package/dist/types/push/pb/Request.d.ts +1 -0
- package/dist/types/push/proto-message.d.ts +4 -1
- package/dist/types/push/push-client.d.ts +33 -0
- package/dist/types/trade/trade-client.d.ts +12 -3
- package/dist/types/version.d.ts +1 -1
- package/package.json +6 -4
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Categorized errors from OAuth 2.0 authorization flows.
|
|
3
|
+
*
|
|
4
|
+
* Authorization server error details are retained in the error message.
|
|
5
|
+
*/
|
|
6
|
+
/** Error category. Each one implies a different action. */
|
|
7
|
+
export declare enum OAuth2Category {
|
|
8
|
+
/** Cannot fetch authorization server metadata: wrong address, service down, no network */
|
|
9
|
+
MetadataUnavailable = "METADATA_UNAVAILABLE",
|
|
10
|
+
/** No usable client_id, and automatic registration is not possible */
|
|
11
|
+
ClientRegistrationRequired = "CLIENT_REGISTRATION_REQUIRED",
|
|
12
|
+
/** Callback stage failed: timeout, user denial, or state mismatch */
|
|
13
|
+
CallbackFailed = "CALLBACK_FAILED",
|
|
14
|
+
/**
|
|
15
|
+
* Device flow not approved in time, or explicitly denied. Kept separate from
|
|
16
|
+
* ReauthorizationRequired because only a new device authorization is needed -- the
|
|
17
|
+
* stored token was not touched.
|
|
18
|
+
*/
|
|
19
|
+
DeviceAuthorizationFailed = "DEVICE_AUTHORIZATION_FAILED",
|
|
20
|
+
/** refresh_token expired, revoked, or never obtained. Retrying is pointless */
|
|
21
|
+
ReauthorizationRequired = "REAUTHORIZATION_REQUIRED",
|
|
22
|
+
/** Token endpoint call failed, and not in the terminal way above */
|
|
23
|
+
TokenRequestFailed = "TOKEN_REQUEST_FAILED",
|
|
24
|
+
/** Reading or writing the local token file failed */
|
|
25
|
+
StorageFailed = "STORAGE_FAILED"
|
|
26
|
+
}
|
|
27
|
+
/** Thrown by any stage of the OAuth2 flow. */
|
|
28
|
+
export declare class OAuth2Error extends Error {
|
|
29
|
+
readonly category: OAuth2Category;
|
|
30
|
+
readonly cause?: unknown;
|
|
31
|
+
constructor(category: OAuth2Category, message: string, cause?: unknown);
|
|
32
|
+
get reauthorizationRequired(): boolean;
|
|
33
|
+
toString(): string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A grant the authorization server refused.
|
|
37
|
+
*
|
|
38
|
+
* Carries the error code so a caller can tell a rejected credential from a rejected
|
|
39
|
+
* client registration. Its category is `ReauthorizationRequired`, so callers that only
|
|
40
|
+
* know the public type behave as they would for any terminal authorization failure.
|
|
41
|
+
*/
|
|
42
|
+
export declare class RejectedGrantError extends OAuth2Error {
|
|
43
|
+
readonly error: string;
|
|
44
|
+
constructor(error: string, description: string);
|
|
45
|
+
get isClientRejected(): boolean;
|
|
46
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every call here is a small JSON exchange with the authorization server, so a request
|
|
3
|
+
* that has not answered in this long is not going to. Same values as the Java and
|
|
4
|
+
* Python SDKs.
|
|
5
|
+
*/
|
|
6
|
+
export declare const CONNECT_TIMEOUT_SECONDS = 5;
|
|
7
|
+
export declare const READ_TIMEOUT_SECONDS = 10;
|
|
8
|
+
/** Form fields whose value is null or undefined are skipped. */
|
|
9
|
+
export type FormFields = Record<string, string | undefined | null>;
|
|
10
|
+
export declare function getJson(url: string): Promise<Record<string, unknown>>;
|
|
11
|
+
export declare function postJson(url: string, payload: unknown): Promise<Record<string, unknown>>;
|
|
12
|
+
export declare function postForm(url: string, form: FormFields): Promise<Record<string, unknown>>;
|
|
13
|
+
/**
|
|
14
|
+
* Post a form and return the HTTP status without requiring a response body.
|
|
15
|
+
*
|
|
16
|
+
* RFC 7009 revocation returns an empty success body, so no parsing is performed.
|
|
17
|
+
*
|
|
18
|
+
* @returns the HTTP status code
|
|
19
|
+
* @throws OAuth2Error on connection failure
|
|
20
|
+
*/
|
|
21
|
+
export declare function postFormForStatus(url: string, form: FormFields): Promise<number>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** OAuth 2.0 authentication, authorization flows, and token persistence. */
|
|
2
|
+
export { OAuth2ClientRegistration } from './client-registration';
|
|
3
|
+
export type { OAuth2ClientRegistrationJson } from './client-registration';
|
|
4
|
+
export { OAuth2DeviceAuthorization, DEFAULT_DEVICE_INTERVAL_SECONDS } from './device-authorization';
|
|
5
|
+
export type { OAuth2DeviceAuthorizationJson } from './device-authorization';
|
|
6
|
+
export { OAuth2Category, OAuth2Error, RejectedGrantError } from './errors';
|
|
7
|
+
export { LoopbackReceiver, CALLBACK_PATH } from './loopback-receiver';
|
|
8
|
+
export { OAuth2Metadata } from './metadata';
|
|
9
|
+
export { OAuth2Authentication, BEARER_PREFIX } from './oauth2-authentication';
|
|
10
|
+
export { OAuth2SessionManager, BUILTIN_ISSUER, CLIENT_NAME, DEFAULT_AUTHORIZE_TIMEOUT_SECONDS, DEFAULT_MANUAL_CALLBACK_PORT, DEFAULT_REFRESH_AHEAD_MILLIS, ENV_ISSUER, MIN_USABLE_REMAINING_MILLIS, } from './session-manager';
|
|
11
|
+
export type { AuthorizationUrlHandler, DeviceAuthorizationHandler, OAuth2SessionManagerOptions, } from './session-manager';
|
|
12
|
+
export { OAuth2Token, TOKEN_SCHEMA_VERSION, currentMillis } from './token';
|
|
13
|
+
export type { OAuth2TokenJson } from './token';
|
|
14
|
+
export { OAuth2TokenStore, DEFAULT_SURFACE, ENV_HOME, hashIssuer } from './token-store';
|
|
15
|
+
export type { TokenStore } from './token-store';
|
|
16
|
+
export { CONNECT_TIMEOUT_SECONDS, READ_TIMEOUT_SECONDS } from './http-utils';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare const CALLBACK_PATH = "/callback";
|
|
2
|
+
/** Single-shot local callback server. */
|
|
3
|
+
export declare class LoopbackReceiver {
|
|
4
|
+
private readonly server;
|
|
5
|
+
private port;
|
|
6
|
+
private params;
|
|
7
|
+
private fired;
|
|
8
|
+
private waiters;
|
|
9
|
+
private constructor();
|
|
10
|
+
/**
|
|
11
|
+
* Bind the receiver. It must be listening before the authorization request is issued.
|
|
12
|
+
*
|
|
13
|
+
* @param port port to bind; 0 lets the system assign a free one
|
|
14
|
+
*/
|
|
15
|
+
static start(port?: number): Promise<LoopbackReceiver>;
|
|
16
|
+
get redirectUri(): string;
|
|
17
|
+
get boundPort(): number;
|
|
18
|
+
/**
|
|
19
|
+
* Wait for the browser callback.
|
|
20
|
+
*
|
|
21
|
+
* @returns the query parameters carried by the callback
|
|
22
|
+
* @throws OAuth2Error on timeout
|
|
23
|
+
*/
|
|
24
|
+
waitForCallback(timeoutSeconds: number): Promise<Record<string, string>>;
|
|
25
|
+
close(): void;
|
|
26
|
+
/** Record the callback params (first one wins) and return a snapshot of them. */
|
|
27
|
+
private record;
|
|
28
|
+
private release;
|
|
29
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authorization server metadata (RFC 8414).
|
|
3
|
+
*
|
|
4
|
+
* Endpoints always come from metadata, never hardcoded, so switching environments only
|
|
5
|
+
* means changing the issuer.
|
|
6
|
+
*/
|
|
7
|
+
/** Raw shape of `{issuer}/.well-known/oauth-authorization-server`. */
|
|
8
|
+
export interface OAuth2MetadataJson {
|
|
9
|
+
issuer?: string;
|
|
10
|
+
authorization_endpoint?: string;
|
|
11
|
+
token_endpoint?: string;
|
|
12
|
+
registration_endpoint?: string;
|
|
13
|
+
device_authorization_endpoint?: string;
|
|
14
|
+
revocation_endpoint?: string;
|
|
15
|
+
scopes_supported?: string[];
|
|
16
|
+
}
|
|
17
|
+
export declare class OAuth2Metadata {
|
|
18
|
+
issuer?: string;
|
|
19
|
+
authorizationEndpoint?: string;
|
|
20
|
+
tokenEndpoint?: string;
|
|
21
|
+
/** Absent means only a manually configured clientId can be used */
|
|
22
|
+
registrationEndpoint?: string;
|
|
23
|
+
/** Absent means this environment does not support the device flow */
|
|
24
|
+
deviceAuthorizationEndpoint?: string;
|
|
25
|
+
/** RFC 7009 revocation. Absent means logout can only clear the local token */
|
|
26
|
+
revocationEndpoint?: string;
|
|
27
|
+
scopesSupported: string[];
|
|
28
|
+
constructor(init?: Partial<Omit<OAuth2Metadata, 'scopesSupported'>> & {
|
|
29
|
+
scopesSupported?: string[];
|
|
30
|
+
});
|
|
31
|
+
static fromJSON(data: OAuth2MetadataJson | null | undefined): OAuth2Metadata | null;
|
|
32
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Request authentication backed by an OAuth 2.0 session manager. */
|
|
2
|
+
import { AuthenticationAttempt, AuthenticationType, RequestAuthContext, RetryDecision, type Authentication } from '../authentication';
|
|
3
|
+
import type { OAuth2SessionManager } from './session-manager';
|
|
4
|
+
export declare const BEARER_PREFIX = "Bearer ";
|
|
5
|
+
/** Authenticate with an OAuth2 access token. */
|
|
6
|
+
export declare class OAuth2Authentication implements Authentication {
|
|
7
|
+
private readonly sessions;
|
|
8
|
+
constructor(sessionManager: OAuth2SessionManager);
|
|
9
|
+
get authType(): AuthenticationType;
|
|
10
|
+
get sessionManager(): OAuth2SessionManager;
|
|
11
|
+
/**
|
|
12
|
+
* Attach a Bearer token to the request context.
|
|
13
|
+
*
|
|
14
|
+
* Near-expiry refreshes use the session manager's single-flight synchronization.
|
|
15
|
+
* Missing authorization rejects with `ReauthorizationRequired` without starting an
|
|
16
|
+
* implicit browser flow.
|
|
17
|
+
*/
|
|
18
|
+
apply(context: RequestAuthContext): Promise<AuthenticationAttempt>;
|
|
19
|
+
/**
|
|
20
|
+
* Process HTTP 401 responses.
|
|
21
|
+
*
|
|
22
|
+
* HTTP 403 indicates insufficient permission and cannot be resolved by refreshing a
|
|
23
|
+
* token with the same scope. Server errors and timeouts are not credential failures.
|
|
24
|
+
*/
|
|
25
|
+
onUnauthorized(attempt: AuthenticationAttempt | undefined, statusCode: number): Promise<RetryDecision>;
|
|
26
|
+
/**
|
|
27
|
+
* The session is owned by the application and may still be in use, so it is not
|
|
28
|
+
* closed here.
|
|
29
|
+
*/
|
|
30
|
+
close(): void;
|
|
31
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { OAuth2DeviceAuthorization } from './device-authorization';
|
|
2
|
+
import { OAuth2Token } from './token';
|
|
3
|
+
import { type TokenStore } from './token-store';
|
|
4
|
+
export declare const ENV_ISSUER = "TIGEROPEN_OAUTH2_ISSUER";
|
|
5
|
+
/**
|
|
6
|
+
* Production authorization server. The environment variable or constructor argument
|
|
7
|
+
* overrides this value.
|
|
8
|
+
*/
|
|
9
|
+
export declare const BUILTIN_ISSUER = "https://openapi-oauth2.tigerfintech.com";
|
|
10
|
+
/** How early to refresh an access token. */
|
|
11
|
+
export declare const DEFAULT_REFRESH_AHEAD_MILLIS: number;
|
|
12
|
+
/** Remaining lifetime below which a token is no longer handed out. */
|
|
13
|
+
export declare const MIN_USABLE_REMAINING_MILLIS: number;
|
|
14
|
+
/** How long to wait for the browser callback. */
|
|
15
|
+
export declare const DEFAULT_AUTHORIZE_TIMEOUT_SECONDS: number;
|
|
16
|
+
/**
|
|
17
|
+
* Fixed callback port for a manually configured clientId. Chosen to stay clear of the
|
|
18
|
+
* default Linux ephemeral range (from 32768) and of common dev ports.
|
|
19
|
+
*/
|
|
20
|
+
export declare const DEFAULT_MANUAL_CALLBACK_PORT = 18888;
|
|
21
|
+
/**
|
|
22
|
+
* Application name registered by the SDK itself. Shown on the user's authorization
|
|
23
|
+
* page, so an entry point that registers its own client passes its own name.
|
|
24
|
+
*/
|
|
25
|
+
export declare const CLIENT_NAME = "Tiger TypeScript SDK";
|
|
26
|
+
/** Receives the authorization URL. The SDK does not open a browser itself. */
|
|
27
|
+
export type AuthorizationUrlHandler = (url: string) => void | Promise<void>;
|
|
28
|
+
/** Receives the device authorization so the caller can display it. */
|
|
29
|
+
export type DeviceAuthorizationHandler = (device: OAuth2DeviceAuthorization) => void | Promise<void>;
|
|
30
|
+
export interface OAuth2SessionManagerOptions {
|
|
31
|
+
/** Authorization server; defaults to `TIGEROPEN_OAUTH2_ISSUER` or the built-in issuer */
|
|
32
|
+
issuer?: string;
|
|
33
|
+
/** Manually issued client ID; omit for dynamic registration */
|
|
34
|
+
clientId?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Loopback port; 0 selects a system-assigned port for dynamic registration. Do not
|
|
37
|
+
* pass one in dynamic registration mode: it pins the registered redirect_uri to that
|
|
38
|
+
* port, and later dropping the argument leaves the registration behind.
|
|
39
|
+
*/
|
|
40
|
+
callbackPort?: number;
|
|
41
|
+
/**
|
|
42
|
+
* Storage root; defaults to `TIGEROPEN_HOME` or `~/.tiger/openapi`. Ignored when
|
|
43
|
+
* `store` is provided
|
|
44
|
+
*/
|
|
45
|
+
home?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Persistence provider; when given, it owns its own surface and `surface` is ignored
|
|
48
|
+
*/
|
|
49
|
+
store?: TokenStore;
|
|
50
|
+
/**
|
|
51
|
+
* Entry point owning the dynamic registration, e.g. `sdk-typescript`, `cli`, `mcp`.
|
|
52
|
+
* Each surface registers its own client, so it appears separately on the user's
|
|
53
|
+
* authorization page and can be revoked on its own. Tools built on this SDK must pass
|
|
54
|
+
* their own value
|
|
55
|
+
*/
|
|
56
|
+
surface?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Application name sent with dynamic registration and shown on the user's
|
|
59
|
+
* authorization page. A tool passing its own `surface` passes its own name too, or
|
|
60
|
+
* the user sees the SDK's name where the tool's belongs
|
|
61
|
+
*/
|
|
62
|
+
clientName?: string;
|
|
63
|
+
/** Loopback callback timeout in seconds */
|
|
64
|
+
authorizeTimeoutSeconds?: number;
|
|
65
|
+
}
|
|
66
|
+
/** OAuth 2.0 authorization session manager. */
|
|
67
|
+
export declare class OAuth2SessionManager {
|
|
68
|
+
private readonly _issuer;
|
|
69
|
+
private readonly _callbackPort;
|
|
70
|
+
/** Fixed for the session because it determines storage keys and registration mode. */
|
|
71
|
+
private readonly explicitClientId;
|
|
72
|
+
private readonly authorizeTimeoutSeconds;
|
|
73
|
+
private readonly clientName;
|
|
74
|
+
private readonly store;
|
|
75
|
+
private registeredClientId;
|
|
76
|
+
private metadata;
|
|
77
|
+
private token;
|
|
78
|
+
private resolvedScopes;
|
|
79
|
+
/** In-flight refresh, so concurrent callers share one token request (single-flight). */
|
|
80
|
+
private refreshInFlight;
|
|
81
|
+
/** In-flight metadata discovery, so concurrent callers share one round trip. */
|
|
82
|
+
private metadataInFlight;
|
|
83
|
+
constructor(options?: OAuth2SessionManagerOptions);
|
|
84
|
+
static defaultIssuer(): string;
|
|
85
|
+
get issuer(): string;
|
|
86
|
+
/** Currently known clientId; undefined before registration in dynamic mode. */
|
|
87
|
+
get clientId(): string | undefined;
|
|
88
|
+
get hasExplicitClientId(): boolean;
|
|
89
|
+
get callbackPort(): number;
|
|
90
|
+
/** Scopes requested by this session, space-separated. Fetches metadata. */
|
|
91
|
+
scopes(): Promise<string>;
|
|
92
|
+
/**
|
|
93
|
+
* Create a default session and perform browser authorization when required.
|
|
94
|
+
*
|
|
95
|
+
* The callback receives the authorization URL; the SDK does not open a browser.
|
|
96
|
+
*/
|
|
97
|
+
static loginWithDefaults(onAuthorizationUrl: AuthorizationUrlHandler): Promise<OAuth2SessionManager>;
|
|
98
|
+
/** Create a default session and perform device authorization when required. */
|
|
99
|
+
static loginWithDeviceCodeDefaults(onDeviceAuthorization: DeviceAuthorizationHandler): Promise<OAuth2SessionManager>;
|
|
100
|
+
/**
|
|
101
|
+
* Perform browser authorization with a loopback redirect and PKCE.
|
|
102
|
+
*
|
|
103
|
+
* @param onAuthorizationUrl callback receiving the authorization URL
|
|
104
|
+
*/
|
|
105
|
+
login(onAuthorizationUrl: AuthorizationUrlHandler): Promise<OAuth2Token>;
|
|
106
|
+
/**
|
|
107
|
+
* Use the existing local authorization if usable, otherwise run a full loopback +
|
|
108
|
+
* PKCE authorization.
|
|
109
|
+
*/
|
|
110
|
+
loginIfNeeded(onAuthorizationUrl: AuthorizationUrlHandler): Promise<OAuth2Token>;
|
|
111
|
+
/**
|
|
112
|
+
* Reuse local authorization or perform device authorization (RFC 8628).
|
|
113
|
+
*
|
|
114
|
+
* Device authorization supports environments without a local browser. Unlike loopback
|
|
115
|
+
* authorization with PKCE, it has no redirect binding or proof that the initiating and
|
|
116
|
+
* redeeming clients are the same party, increasing phishing risk. Loopback
|
|
117
|
+
* authorization is preferred when a local browser is available.
|
|
118
|
+
*/
|
|
119
|
+
loginWithDeviceCodeIfNeeded(onDeviceAuthorization: DeviceAuthorizationHandler): Promise<OAuth2Token>;
|
|
120
|
+
/**
|
|
121
|
+
* Perform device authorization as defined by RFC 8628.
|
|
122
|
+
*
|
|
123
|
+
* This flow is intended for environments without a local browser and has the security
|
|
124
|
+
* limitations documented by {@link loginWithDeviceCodeIfNeeded}.
|
|
125
|
+
*/
|
|
126
|
+
loginWithDeviceCode(onDeviceAuthorization: DeviceAuthorizationHandler): Promise<OAuth2Token>;
|
|
127
|
+
/**
|
|
128
|
+
* Revoke authorization under RFC 7009 and delete the local token.
|
|
129
|
+
*
|
|
130
|
+
* Dynamic client registration is retained across logout. The local token is deleted
|
|
131
|
+
* even if server-side revocation fails.
|
|
132
|
+
*
|
|
133
|
+
* @returns true when server-side revocation succeeds; false when only local state is
|
|
134
|
+
* cleared and the server credential may remain valid
|
|
135
|
+
*/
|
|
136
|
+
logout(): Promise<boolean>;
|
|
137
|
+
/**
|
|
138
|
+
* Return a usable access token, refreshing near expiry.
|
|
139
|
+
*
|
|
140
|
+
* Throws `ReauthorizationRequired` when no authorization is available. Browser
|
|
141
|
+
* authorization is never started implicitly.
|
|
142
|
+
*
|
|
143
|
+
* A refresh being due does not make the caller wait for one. While the current token
|
|
144
|
+
* still has {@link MIN_USABLE_REMAINING_MILLIS} of life, a refresh already running
|
|
145
|
+
* elsewhere is left to finish and the current token is returned.
|
|
146
|
+
*/
|
|
147
|
+
getAccessToken(): Promise<string>;
|
|
148
|
+
/** The current token, or undefined. Sends no requests and triggers no authorization. */
|
|
149
|
+
status(): OAuth2Token | undefined;
|
|
150
|
+
/**
|
|
151
|
+
* Whether asking for a token now would trigger a refresh. Reads memory only.
|
|
152
|
+
*
|
|
153
|
+
* Exists so no other component needs a refresh-ahead window of its own.
|
|
154
|
+
*/
|
|
155
|
+
shouldRefreshSoon(): boolean;
|
|
156
|
+
isAuthorized(): boolean;
|
|
157
|
+
/** Refresh because the token is close to expiry. */
|
|
158
|
+
refresh(failedAccessToken?: string): Promise<OAuth2Token>;
|
|
159
|
+
/**
|
|
160
|
+
* Refresh after a 401 even when the token is not near expiry.
|
|
161
|
+
*
|
|
162
|
+
* @param failedAccessToken token used by the failed request; comparison with the
|
|
163
|
+
* current token detects rotation completed elsewhere and preserves single-flight
|
|
164
|
+
* refresh behavior
|
|
165
|
+
*/
|
|
166
|
+
refreshAfterUnauthorized(failedAccessToken: string | undefined): Promise<OAuth2Token>;
|
|
167
|
+
/**
|
|
168
|
+
* Single-flight refresh: the first caller performs rotation while others await the
|
|
169
|
+
* same promise.
|
|
170
|
+
*/
|
|
171
|
+
private doRefresh;
|
|
172
|
+
private refreshOnce;
|
|
173
|
+
/**
|
|
174
|
+
* Handle a refresh the authorization server rejected.
|
|
175
|
+
*
|
|
176
|
+
* Refresh tokens rotate, so the rejected value may already have been replaced by
|
|
177
|
+
* another process sharing this store. The store is re-read before any local state is
|
|
178
|
+
* removed, and the authorization is treated as ended only once the newest stored
|
|
179
|
+
* credential is rejected too.
|
|
180
|
+
*/
|
|
181
|
+
private recoverOrGiveUp;
|
|
182
|
+
/** Exchange the given token's refresh token for a new access token and persist it. */
|
|
183
|
+
private exchangeRefreshToken;
|
|
184
|
+
/**
|
|
185
|
+
* Read the stored token from disk, bypassing the in-memory copy.
|
|
186
|
+
*
|
|
187
|
+
* @returns the stored token, or null when there is none or it cannot be read
|
|
188
|
+
*/
|
|
189
|
+
private reloadFromStore;
|
|
190
|
+
/**
|
|
191
|
+
* Remove the local token, and the dynamic registration when that was refused.
|
|
192
|
+
*
|
|
193
|
+
* Only the credential that was refused is removed, and only while it is still the one
|
|
194
|
+
* on disk. Another process sharing the store may have replaced it in the meantime, and
|
|
195
|
+
* that replacement was never refused.
|
|
196
|
+
*/
|
|
197
|
+
private forgetRejectedAuthorization;
|
|
198
|
+
/**
|
|
199
|
+
* Remove a refused dynamic client registration so the next authorization registers a
|
|
200
|
+
* new one. Explicit clientIds are left alone.
|
|
201
|
+
*/
|
|
202
|
+
private forgetRejectedRegistration;
|
|
203
|
+
/**
|
|
204
|
+
* Return a reusable local token, refreshing it when required.
|
|
205
|
+
*
|
|
206
|
+
* @returns undefined when interactive authorization is required
|
|
207
|
+
*/
|
|
208
|
+
private reusableToken;
|
|
209
|
+
private currentToken;
|
|
210
|
+
/**
|
|
211
|
+
* The known clientId, without triggering registration. Manual mode never reads
|
|
212
|
+
* `clients/`.
|
|
213
|
+
*/
|
|
214
|
+
private knownClientId;
|
|
215
|
+
private persist;
|
|
216
|
+
private effectiveCallbackPort;
|
|
217
|
+
private resolveScopes;
|
|
218
|
+
private metadataOf;
|
|
219
|
+
/** Manual clientId > saved registration whose scopes still cover > register. */
|
|
220
|
+
private resolveClientId;
|
|
221
|
+
/**
|
|
222
|
+
* Register a public client under RFC 7591.
|
|
223
|
+
*
|
|
224
|
+
* Registration omits scope. The granted scope set is stored separately so a wider
|
|
225
|
+
* requirement triggers re-registration instead of `invalid_scope`.
|
|
226
|
+
*/
|
|
227
|
+
private register;
|
|
228
|
+
private requestDeviceAuthorization;
|
|
229
|
+
/** Poll the token endpoint according to RFC 8628 response semantics. */
|
|
230
|
+
private pollForDeviceToken;
|
|
231
|
+
private exchange;
|
|
232
|
+
private toToken;
|
|
233
|
+
/**
|
|
234
|
+
* Attempt server-side revocation without throwing.
|
|
235
|
+
*
|
|
236
|
+
* The refresh token is preferred because RFC 7009 §2.1 permits revocation of its
|
|
237
|
+
* associated access tokens. RFC 7009 §2.2 also returns success for unknown or
|
|
238
|
+
* previously revoked tokens.
|
|
239
|
+
*/
|
|
240
|
+
private revoke;
|
|
241
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { OAuth2ClientRegistration } from './client-registration';
|
|
2
|
+
import { OAuth2Token } from './token';
|
|
3
|
+
export declare const ENV_HOME = "TIGEROPEN_HOME";
|
|
4
|
+
/**
|
|
5
|
+
* Entry point owning a registration. Tools built on this SDK must pass their own value,
|
|
6
|
+
* otherwise they would be filed as the TypeScript SDK and share its client. Keep in
|
|
7
|
+
* step with the Java SDK's `sdk-java` and the Python SDK's `sdk-python`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const DEFAULT_SURFACE = "sdk-typescript";
|
|
10
|
+
/** Persistence provider contract, so a caller can store tokens somewhere else. */
|
|
11
|
+
export interface TokenStore {
|
|
12
|
+
loadToken(clientId: string, expectedIssuer?: string): OAuth2Token | null;
|
|
13
|
+
saveToken(token: OAuth2Token): void;
|
|
14
|
+
deleteToken(clientId: string): string | null;
|
|
15
|
+
loadClientId(issuer: string): string | null;
|
|
16
|
+
loadClientRegistration(issuer: string): OAuth2ClientRegistration | null;
|
|
17
|
+
saveClientRegistration(registration: OAuth2ClientRegistration): void;
|
|
18
|
+
deleteClientRegistration(issuer: string): void;
|
|
19
|
+
}
|
|
20
|
+
export declare class OAuth2TokenStore implements TokenStore {
|
|
21
|
+
readonly home: string;
|
|
22
|
+
readonly surface: string;
|
|
23
|
+
/**
|
|
24
|
+
* @param home storage root; defaults to `TIGEROPEN_HOME` then `~/.tiger/openapi`
|
|
25
|
+
* @param surface entry point owning the registration, e.g. `sdk-typescript`, `cli`.
|
|
26
|
+
* Lowercase alphanumerics and hyphens only
|
|
27
|
+
*/
|
|
28
|
+
constructor(home?: string, surface?: string);
|
|
29
|
+
/** Load a token, treating a missing file or issuer mismatch as a cache miss. */
|
|
30
|
+
loadToken(clientId: string, expectedIssuer?: string): OAuth2Token | null;
|
|
31
|
+
saveToken(token: OAuth2Token): void;
|
|
32
|
+
/**
|
|
33
|
+
* Delete authorization state while retaining client registration.
|
|
34
|
+
*
|
|
35
|
+
* @returns the path removed, or null when there was nothing to remove. A clientId is
|
|
36
|
+
* not its own file name, so a caller reporting what it deleted has to be told rather
|
|
37
|
+
* than build the path itself
|
|
38
|
+
*/
|
|
39
|
+
deleteToken(clientId: string): string | null;
|
|
40
|
+
loadClientId(issuer: string): string | null;
|
|
41
|
+
loadClientRegistration(issuer: string): OAuth2ClientRegistration | null;
|
|
42
|
+
saveClientRegistration(registration: OAuth2ClientRegistration): void;
|
|
43
|
+
/** Delete registration state after an `invalid_client` response. */
|
|
44
|
+
deleteClientRegistration(issuer: string): void;
|
|
45
|
+
private tokenFile;
|
|
46
|
+
private clientFile;
|
|
47
|
+
}
|
|
48
|
+
/** Return the Java-compatible 32-character SHA-256 issuer file key. */
|
|
49
|
+
export declare function hashIssuer(issuer: string): string;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persisted OAuth 2.0 authorization state.
|
|
3
|
+
*
|
|
4
|
+
* The `issuer` and `clientId` bind credentials to an authorization server and client,
|
|
5
|
+
* preventing credential reuse across environments. The on-disk field names are
|
|
6
|
+
* snake_case and shared with the Java and Python SDKs.
|
|
7
|
+
*/
|
|
8
|
+
/** On-disk format version. An unrecognised version is an error, not a guess. */
|
|
9
|
+
export declare const TOKEN_SCHEMA_VERSION = 1;
|
|
10
|
+
export declare function currentMillis(): number;
|
|
11
|
+
/** Raw on-disk shape. */
|
|
12
|
+
export interface OAuth2TokenJson {
|
|
13
|
+
schema_version?: number;
|
|
14
|
+
issuer?: string;
|
|
15
|
+
client_id?: string;
|
|
16
|
+
access_token?: string;
|
|
17
|
+
refresh_token?: string;
|
|
18
|
+
token_type?: string;
|
|
19
|
+
scope?: string;
|
|
20
|
+
expires_at?: number;
|
|
21
|
+
}
|
|
22
|
+
/** An access token and its metadata. */
|
|
23
|
+
export declare class OAuth2Token {
|
|
24
|
+
schemaVersion: number;
|
|
25
|
+
issuer?: string;
|
|
26
|
+
clientId?: string;
|
|
27
|
+
accessToken?: string;
|
|
28
|
+
refreshToken?: string;
|
|
29
|
+
tokenType: string;
|
|
30
|
+
scope?: string;
|
|
31
|
+
/** Absolute expiry as epoch millis, not expires_in */
|
|
32
|
+
expiresAt: number;
|
|
33
|
+
constructor(init?: {
|
|
34
|
+
issuer?: string;
|
|
35
|
+
clientId?: string;
|
|
36
|
+
accessToken?: string;
|
|
37
|
+
refreshToken?: string;
|
|
38
|
+
tokenType?: string;
|
|
39
|
+
scope?: string;
|
|
40
|
+
expiresAt?: number;
|
|
41
|
+
schemaVersion?: number;
|
|
42
|
+
});
|
|
43
|
+
/** Time until expiry; negative once already expired. */
|
|
44
|
+
remainingMillis(): number;
|
|
45
|
+
/**
|
|
46
|
+
* @param aheadMillis how early to refresh. The window exists because time passes
|
|
47
|
+
* between sending a request and the server validating it, so using a token right up
|
|
48
|
+
* to its expiry produces intermittent 401s
|
|
49
|
+
*/
|
|
50
|
+
needsRefresh(aheadMillis: number): boolean;
|
|
51
|
+
hasRefreshToken(): boolean;
|
|
52
|
+
toJSON(): OAuth2TokenJson;
|
|
53
|
+
static fromJSON(data: OAuth2TokenJson | null | undefined): OAuth2Token | null;
|
|
54
|
+
/** Prints no token values. */
|
|
55
|
+
toString(): string;
|
|
56
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export type { ErrorCategory } from './client';
|
|
|
26
26
|
export { QuoteClient, parseOptionIdentifier } from './quote';
|
|
27
27
|
export { TradeClient } from './trade';
|
|
28
28
|
export { PushClient, ConnectionState, SubjectType } from './push';
|
|
29
|
-
export type { PushClientOptions } from './push';
|
|
29
|
+
export type { PushClientOptions, AccountSubscribeOptions } from './push';
|
|
30
30
|
export type { Order, OrderRequest, ContractLegRequest } from './model/order';
|
|
31
31
|
export { marketOrder, limitOrder, stopOrder, stopLimitOrder } from './model/order-helpers';
|
|
32
32
|
export { normalizeOrderStatus } from './model/order-status';
|
|
@@ -72,6 +72,7 @@ export interface PositionsRequest {
|
|
|
72
72
|
expiry?: string;
|
|
73
73
|
strike?: string;
|
|
74
74
|
right?: string;
|
|
75
|
+
/** 资产行情类型枚举名: ETH / RTH / OVERNIGHT (wire: asset_quote_type) */
|
|
75
76
|
assetQuoteType?: string;
|
|
76
77
|
lang?: string;
|
|
77
78
|
}
|
|
@@ -82,6 +83,8 @@ export interface AssetsRequest {
|
|
|
82
83
|
subAccounts?: string[];
|
|
83
84
|
segment?: boolean;
|
|
84
85
|
marketValue?: boolean;
|
|
86
|
+
/** 资产行情类型枚举名: ETH / RTH / OVERNIGHT (wire: asset_quote_type) */
|
|
87
|
+
assetQuoteType?: string;
|
|
85
88
|
lang?: string;
|
|
86
89
|
}
|
|
87
90
|
export interface ManagedAccountsRequest {
|
|
@@ -115,6 +118,8 @@ export interface AggregateAssetsRequest {
|
|
|
115
118
|
secretKey?: string;
|
|
116
119
|
baseCurrency?: string;
|
|
117
120
|
segType?: string;
|
|
121
|
+
/** 资产行情类型枚举名: ETH / RTH / OVERNIGHT (wire: asset_quote_type) */
|
|
122
|
+
assetQuoteType?: string;
|
|
118
123
|
lang?: string;
|
|
119
124
|
}
|
|
120
125
|
export interface EstimateTradableQuantityRequest {
|
|
@@ -7,4 +7,4 @@ export * from './varint';
|
|
|
7
7
|
export * from './tick-util';
|
|
8
8
|
export * from './proto-message';
|
|
9
9
|
export { PushClient, ConnectionState } from './push-client';
|
|
10
|
-
export type { PushClientOptions, TLSSocketLike, TLSSocketFactory } from './push-client';
|
|
10
|
+
export type { PushClientOptions, AccountSubscribeOptions, TLSSocketLike, TLSSocketFactory, } from './push-client';
|
|
@@ -21,6 +21,7 @@ export interface Request_Subscribe {
|
|
|
21
21
|
symbols?: string | undefined;
|
|
22
22
|
account?: string | undefined;
|
|
23
23
|
market?: string | undefined;
|
|
24
|
+
assetQuoteType?: string | undefined;
|
|
24
25
|
}
|
|
25
26
|
export declare const Request: MessageFns<Request>;
|
|
26
27
|
export declare const Request_Connect: MessageFns<Request_Connect>;
|
|
@@ -17,8 +17,11 @@ export declare function buildConnectMessage(tigerId: string, sign: string, sdkVe
|
|
|
17
17
|
export declare function buildHeartBeatMessage(): Request;
|
|
18
18
|
/**
|
|
19
19
|
* 构建 SUBSCRIBE 请求消息
|
|
20
|
+
*
|
|
21
|
+
* @param assetQuoteType 资产行情类型枚举名(ETH / RTH / OVERNIGHT),
|
|
22
|
+
* 仅对 Asset / Position 两种订阅有意义,其余场景传 undefined
|
|
20
23
|
*/
|
|
21
|
-
export declare function buildSubscribeMessage(dataType: SocketCommon_DataType, symbols?: string, account?: string, market?: string): Request;
|
|
24
|
+
export declare function buildSubscribeMessage(dataType: SocketCommon_DataType, symbols?: string, account?: string, market?: string, assetQuoteType?: string): Request;
|
|
22
25
|
/**
|
|
23
26
|
* 构建 UNSUBSCRIBE 请求消息
|
|
24
27
|
*/
|
|
@@ -7,6 +7,18 @@ export declare enum ConnectionState {
|
|
|
7
7
|
Connecting = 1,
|
|
8
8
|
Connected = 2
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Options for account-level subscriptions (asset / position).
|
|
12
|
+
*
|
|
13
|
+
* `assetQuoteType` 用于选择资产/持仓推送的行情口径,通过重载传入,
|
|
14
|
+
* 老的 `subscribeAsset(account?)` / `subscribePosition(account?)` 签名保持不变。
|
|
15
|
+
*/
|
|
16
|
+
export interface AccountSubscribeOptions {
|
|
17
|
+
/** Account to subscribe; omit to use the account from ClientConfig. */
|
|
18
|
+
account?: string;
|
|
19
|
+
/** 资产行情类型枚举名: ETH / RTH / OVERNIGHT,省略则由服务端取默认值 */
|
|
20
|
+
assetQuoteType?: string;
|
|
21
|
+
}
|
|
10
22
|
/** PushClient configuration options */
|
|
11
23
|
export interface PushClientOptions {
|
|
12
24
|
pushHost?: string;
|
|
@@ -51,6 +63,8 @@ export declare class PushClient {
|
|
|
51
63
|
private subscriptions;
|
|
52
64
|
/** Account-level subscriptions: subject -> account string used at subscribe time */
|
|
53
65
|
private accountSubs;
|
|
66
|
+
/** Account-level subscriptions: subject -> assetQuoteType used at subscribe time (reconnect replay) */
|
|
67
|
+
private accountSubQuoteTypes;
|
|
54
68
|
private heartbeatTimer;
|
|
55
69
|
private reconnectTimer;
|
|
56
70
|
private stopped;
|
|
@@ -150,12 +164,31 @@ export declare class PushClient {
|
|
|
150
164
|
subscribeMarket(market: string): void;
|
|
151
165
|
/** Unsubscribe from market status (v0.4.0). */
|
|
152
166
|
unsubscribeMarket(market: string): void;
|
|
167
|
+
/**
|
|
168
|
+
* Shared implementation for the account/options overloads of
|
|
169
|
+
* subscribeAsset / subscribePosition.
|
|
170
|
+
*/
|
|
171
|
+
private subscribeAccount;
|
|
153
172
|
/** Subscribe to asset changes */
|
|
154
173
|
subscribeAsset(account?: string): void;
|
|
174
|
+
/**
|
|
175
|
+
* Subscribe to asset changes with extra options.
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* client.subscribeAsset({ assetQuoteType: 'OVERNIGHT' });
|
|
179
|
+
*/
|
|
180
|
+
subscribeAsset(options: AccountSubscribeOptions): void;
|
|
155
181
|
/** Unsubscribe from asset changes */
|
|
156
182
|
unsubscribeAsset(): void;
|
|
157
183
|
/** Subscribe to position changes */
|
|
158
184
|
subscribePosition(account?: string): void;
|
|
185
|
+
/**
|
|
186
|
+
* Subscribe to position changes with extra options.
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* client.subscribePosition({ assetQuoteType: 'OVERNIGHT' });
|
|
190
|
+
*/
|
|
191
|
+
subscribePosition(options: AccountSubscribeOptions): void;
|
|
159
192
|
/** Unsubscribe from position changes */
|
|
160
193
|
unsubscribePosition(): void;
|
|
161
194
|
/** Subscribe to order status */
|