@tigeropenapi/tigeropen 0.5.4 → 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.
Files changed (73) hide show
  1. package/README.md +80 -0
  2. package/dist/cjs/auth/authentication.js +74 -0
  3. package/dist/cjs/auth/index.js +24 -0
  4. package/dist/cjs/auth/oauth2/client-registration.js +44 -0
  5. package/dist/cjs/auth/oauth2/device-authorization.js +40 -0
  6. package/dist/cjs/auth/oauth2/errors.js +64 -0
  7. package/dist/cjs/auth/oauth2/http-utils.js +112 -0
  8. package/dist/cjs/auth/oauth2/index.js +42 -0
  9. package/dist/cjs/auth/oauth2/loopback-receiver.js +215 -0
  10. package/dist/cjs/auth/oauth2/metadata.js +34 -0
  11. package/dist/cjs/auth/oauth2/oauth2-authentication.js +74 -0
  12. package/dist/cjs/auth/oauth2/session-manager.js +762 -0
  13. package/dist/cjs/auth/oauth2/token-store.js +252 -0
  14. package/dist/cjs/auth/oauth2/token.js +76 -0
  15. package/dist/cjs/push/index.js +1 -0
  16. package/dist/cjs/push/pb/Request.js +12 -1
  17. package/dist/cjs/push/proto-message.js +5 -1
  18. package/dist/cjs/push/push-client.js +34 -16
  19. package/dist/cjs/push/tick-util.js +172 -0
  20. package/dist/cjs/quote/quote-client.js +59 -12
  21. package/dist/cjs/trade/trade-client.js +55 -6
  22. package/dist/cjs/version.js +1 -1
  23. package/dist/esm/auth/authentication.js +68 -0
  24. package/dist/esm/auth/index.js +3 -0
  25. package/dist/esm/auth/oauth2/client-registration.js +40 -0
  26. package/dist/esm/auth/oauth2/device-authorization.js +36 -0
  27. package/dist/esm/auth/oauth2/errors.js +59 -0
  28. package/dist/esm/auth/oauth2/http-utils.js +105 -0
  29. package/dist/esm/auth/oauth2/index.js +11 -0
  30. package/dist/esm/auth/oauth2/loopback-receiver.js +178 -0
  31. package/dist/esm/auth/oauth2/metadata.js +30 -0
  32. package/dist/esm/auth/oauth2/oauth2-authentication.js +70 -0
  33. package/dist/esm/auth/oauth2/session-manager.js +725 -0
  34. package/dist/esm/auth/oauth2/token-store.js +214 -0
  35. package/dist/esm/auth/oauth2/token.js +71 -0
  36. package/dist/esm/package.json +3 -0
  37. package/dist/esm/push/index.js +1 -0
  38. package/dist/esm/push/pb/Request.js +12 -1
  39. package/dist/esm/push/proto-message.js +5 -1
  40. package/dist/esm/push/push-client.js +34 -16
  41. package/dist/esm/push/tick-util.js +165 -0
  42. package/dist/esm/quote/quote-client.js +59 -12
  43. package/dist/esm/trade/trade-client.js +55 -6
  44. package/dist/esm/version.js +1 -1
  45. package/dist/types/auth/authentication.d.ts +98 -0
  46. package/dist/types/auth/index.d.ts +4 -0
  47. package/dist/types/auth/oauth2/client-registration.d.ts +32 -0
  48. package/dist/types/auth/oauth2/device-authorization.d.ts +40 -0
  49. package/dist/types/auth/oauth2/errors.d.ts +46 -0
  50. package/dist/types/auth/oauth2/http-utils.d.ts +21 -0
  51. package/dist/types/auth/oauth2/index.d.ts +16 -0
  52. package/dist/types/auth/oauth2/loopback-receiver.d.ts +29 -0
  53. package/dist/types/auth/oauth2/metadata.d.ts +32 -0
  54. package/dist/types/auth/oauth2/oauth2-authentication.d.ts +31 -0
  55. package/dist/types/auth/oauth2/session-manager.d.ts +241 -0
  56. package/dist/types/auth/oauth2/token-store.d.ts +49 -0
  57. package/dist/types/auth/oauth2/token.d.ts +56 -0
  58. package/dist/types/index.d.ts +2 -2
  59. package/dist/types/model/order.d.ts +62 -11
  60. package/dist/types/model/quote-requests.d.ts +21 -2
  61. package/dist/types/model/quote.d.ts +72 -14
  62. package/dist/types/model/trade-requests.d.ts +17 -4
  63. package/dist/types/model/trade.d.ts +11 -1
  64. package/dist/types/push/callbacks.d.ts +3 -7
  65. package/dist/types/push/index.d.ts +2 -1
  66. package/dist/types/push/pb/Request.d.ts +1 -0
  67. package/dist/types/push/proto-message.d.ts +4 -1
  68. package/dist/types/push/push-client.d.ts +33 -0
  69. package/dist/types/push/tick-util.d.ts +30 -0
  70. package/dist/types/quote/quote-client.d.ts +27 -6
  71. package/dist/types/trade/trade-client.d.ts +27 -3
  72. package/dist/types/version.d.ts +1 -1
  73. package/package.json +8 -4
@@ -9,6 +9,7 @@ import { HttpClient } from '../client/http-client.js';
9
9
  import { createApiRequest } from '../client/api-request.js';
10
10
  import { unmarshalData } from '../client/api-response.js';
11
11
  import * as tokenMethods from '../client/token-methods.js';
12
+ import { getTradeCondByCode, isUsStockSymbol } from '../push/tick-util.js';
12
13
  /**
13
14
  * resolveOptionTimezone returns the timezone string to use for expiry conversion.
14
15
  * Explicit tz takes precedence; otherwise infer from symbol (.HK suffix → Asia/Hong_Kong, else America/New_York).
@@ -148,12 +149,22 @@ export class QuoteClient {
148
149
  async getKline(req) {
149
150
  return this.callInto('kline', req);
150
151
  }
151
- async getTimeline(symbols) {
152
- return this.callInto('timeline', { symbols });
152
+ async getTimeline(symbolsOrRequest) {
153
+ const req = Array.isArray(symbolsOrRequest) ? { symbols: symbolsOrRequest } : symbolsOrRequest;
154
+ return this.callInto('timeline', req, req.secType === 'CC' ? '3.0' : undefined);
153
155
  }
154
156
  /** Tick-by-tick trades. wire: trade_tick */
155
157
  async getTradeTick(req) {
156
- return this.callInto('trade_tick', req);
158
+ const out = await this.callInto('trade_tick', req);
159
+ for (const tick of out) {
160
+ const isUs = isUsStockSymbol(tick.symbol);
161
+ for (const item of tick.items) {
162
+ if (item.cond && item.cond.length > 0) {
163
+ item.cond = getTradeCondByCode(isUs, item.cond[0]);
164
+ }
165
+ }
166
+ }
167
+ return out;
157
168
  }
158
169
  /** Depth snapshot. wire: quote_depth */
159
170
  async getQuoteDepth(req) {
@@ -319,6 +330,7 @@ export class QuoteClient {
319
330
  while (acc.length < totalSize) {
320
331
  const sub = {
321
332
  symbols: req.symbol ? [req.symbol] : undefined,
333
+ secType: req.secType,
322
334
  period: req.period,
323
335
  right: req.right,
324
336
  beginTime,
@@ -369,7 +381,7 @@ export class QuoteClient {
369
381
  async getStockIndustry(req) {
370
382
  return this.callInto('stock_industry', req);
371
383
  }
372
- /** Quote permission detail. wire: get_quote_permission */
384
+ /** Market data permission entries. wire: get_quote_permission */
373
385
  async getQuotePermission(req) {
374
386
  return this.callInto('get_quote_permission', req);
375
387
  }
@@ -380,9 +392,16 @@ export class QuoteClient {
380
392
  // ==========================================================================
381
393
  // Batch 4: option / future extensions
382
394
  // ==========================================================================
383
- /** Option tick trades. wire: option_trade_tick */
395
+ /** Option tick trades. wire: option_trade_tick
396
+ *
397
+ * Server expects `biz_content` as a top-level JSON array (matches Java's
398
+ * `BatchApiModel` and Python's `MultipleContractParams.to_openapi_dict`).
399
+ * We unwrap the request here so the array is emitted directly instead of
400
+ * `{"contracts": [...]}`. Sending it as an object triggers
401
+ * `biz param error(failed to parse parameters in 'biz_content')`.
402
+ */
384
403
  async getOptionTradeTicks(req) {
385
- return this.callInto('option_trade_tick', req);
404
+ return this.callInto('option_trade_tick', req.contracts ?? []);
386
405
  }
387
406
  /** Option intraday timeline. wire: option_timeline */
388
407
  async getOptionTimeline(req) {
@@ -421,10 +440,16 @@ export class QuoteClient {
421
440
  return this.callInto('future_main_contract', req);
422
441
  }
423
442
  /** Futures K-line. wire: future_kline
424
- * beginTime / endTime default to -1 when unset (server requires them present). */
443
+ * beginTime / endTime default to -1 when unset (server requires them present).
444
+ * Server expects `contract_codes` (plural) — normalize singular `contractCode`
445
+ * to the plural form so callers can pass either shape. */
425
446
  async getFutureKline(req) {
447
+ const contractCodes = req.contractCodes
448
+ ?? (req.contractCode ? [req.contractCode] : undefined);
449
+ const { contractCode: _drop, ...rest } = req;
426
450
  const body = {
427
- ...req,
451
+ ...rest,
452
+ ...(contractCodes ? { contractCodes } : {}),
428
453
  beginTime: req.beginTime ?? -1,
429
454
  endTime: req.endTime ?? -1,
430
455
  };
@@ -442,8 +467,9 @@ export class QuoteClient {
442
467
  endTime = -1;
443
468
  const acc = [];
444
469
  while (acc.length < totalSize) {
470
+ // Server rejects singular `contract_code` — send the plural form.
445
471
  const sub = {
446
- contractCode: req.contractCode,
472
+ contractCodes: req.contractCode ? [req.contractCode] : undefined,
447
473
  period: req.period,
448
474
  beginTime,
449
475
  endTime,
@@ -522,9 +548,30 @@ export class QuoteClient {
522
548
  async getWarrantFilter(req) {
523
549
  return this.callInto('warrant_filter', req);
524
550
  }
525
- /** Industry list. wire: industry_list */
526
- async getIndustryList(req) {
527
- return this.callInto('industry_list', req);
551
+ /** Industry list. wire: industry_list
552
+ *
553
+ * `industryLevel` is required by the server — omitting it returns
554
+ * `industry level error, all supported is: [GSECTOR, GGROUP, GIND, GSUBIND]`.
555
+ * Defaults to `GGROUP` when unset, matching Python SDK's
556
+ * `IndustryLevel.GGROUP` default.
557
+ *
558
+ * Wire fields differ from other endpoints (see `IndustryItem`): server sends
559
+ * `nameCN` / `nameEN` / `industryLevel`. This method hydrates the
560
+ * backwards-compat `name` (from `nameEN || nameCN`) and mirrors
561
+ * `industryLevel` to `level` so existing callers keep working.
562
+ */
563
+ async getIndustryList(req = {}) {
564
+ const payload = { industryLevel: 'GGROUP', ...req };
565
+ const items = await this.callInto('industry_list', payload);
566
+ if (!Array.isArray(items))
567
+ return [];
568
+ for (const it of items) {
569
+ if (it.name === undefined)
570
+ it.name = it.nameEN || it.nameCN;
571
+ if (it.level === undefined)
572
+ it.level = it.industryLevel;
573
+ }
574
+ return items;
528
575
  }
529
576
  /** Stocks inside an industry. wire: industry_stock_list */
530
577
  async getIndustryStocks(req) {
@@ -109,14 +109,54 @@ export class TradeClient {
109
109
  });
110
110
  }
111
111
  // === Order operations ===
112
+ /**
113
+ * Prepare an order request for the wire.
114
+ *
115
+ * The gateway expects `algo_params` as a `[{tag, value}, ...]` array
116
+ * (matching the Python SDK's AlgoParams.to_dict serialization), not the
117
+ * plain object shape that reads naturally in TypeScript. This helper
118
+ * normalizes the object form into the array form on outbound payloads
119
+ * so callers can pass the natural object and TWAP/VWAP orders work end
120
+ * to end without wire-format workarounds.
121
+ *
122
+ * Fields with undefined/null values are dropped; empty algoParams
123
+ * becomes an empty array rather than being stripped, matching the
124
+ * Python SDK behavior when the user passes an empty AlgoParams().
125
+ */
126
+ marshalOrderForWire(order) {
127
+ const { algoParams, ...rest } = order;
128
+ const withAccount = { ...rest, account: this.account };
129
+ if (algoParams === undefined)
130
+ return withAccount;
131
+ // Already a tag/value array (advanced callers who want raw control)
132
+ if (Array.isArray(algoParams)) {
133
+ withAccount.algoParams = algoParams;
134
+ return withAccount;
135
+ }
136
+ // Object shape → tag/value array.
137
+ // Keys are converted to snake_case explicitly here (e.g. `startTime` →
138
+ // `start_time`) so they land correctly in the `tag` string that the
139
+ // gateway reads. `keysToSnakeCase` only rewrites object *keys*, not
140
+ // string *values*, so `tag` would be left camelCase if we didn't do
141
+ // this conversion ourselves.
142
+ const tagValues = [];
143
+ for (const [key, value] of Object.entries(algoParams)) {
144
+ if (value === undefined || value === null)
145
+ continue;
146
+ const snakeKey = key.replace(/[A-Z]/g, (c) => '_' + c.toLowerCase());
147
+ tagValues.push({ tag: snakeKey, value });
148
+ }
149
+ withAccount.algoParams = tagValues;
150
+ return withAccount;
151
+ }
112
152
  async placeOrder(order) {
113
- return this.callInto('place_order', { ...order, account: this.account });
153
+ return this.callInto('place_order', this.marshalOrderForWire(order));
114
154
  }
115
155
  async previewOrder(order) {
116
- return this.callInto('preview_order', { ...order, account: this.account });
156
+ return this.callInto('preview_order', this.marshalOrderForWire(order));
117
157
  }
118
158
  async modifyOrder(id, order) {
119
- return this.callInto('modify_order', { ...order, account: this.account, id });
159
+ return this.callInto('modify_order', { ...this.marshalOrderForWire(order), id });
120
160
  }
121
161
  async cancelOrder(id, secretKey) {
122
162
  const params = { account: this.account, id };
@@ -181,11 +221,17 @@ export class TradeClient {
181
221
  async getPositions(req) {
182
222
  return this.callIntoItems('positions', withAccount(req, this.account));
183
223
  }
184
- /** Query assets. v0.4.0: supports sub-account list and aggregation options. */
224
+ /**
225
+ * Query assets. v0.4.0: supports sub-account list and aggregation options.
226
+ * `assetQuoteType`(ETH / RTH / OVERNIGHT) 选择资产行情口径。
227
+ */
185
228
  async getAssets(req) {
186
229
  return this.callIntoItems('assets', withAccount(req, this.account));
187
230
  }
188
- /** Query prime assets. v0.4.0: takes AssetsRequest for filters. */
231
+ /**
232
+ * Query prime assets. v0.4.0: takes AssetsRequest for filters.
233
+ * `assetQuoteType`(ETH / RTH / OVERNIGHT) 选择资产行情口径。
234
+ */
189
235
  async getPrimeAssets(req) {
190
236
  return this.callInto('prime_assets', withAccount(req, this.account));
191
237
  }
@@ -206,7 +252,10 @@ export class TradeClient {
206
252
  async getAnalyticsAsset(req) {
207
253
  return this.callIntoItems('analytics_asset', withAccount(req, this.account));
208
254
  }
209
- /** Aggregate assets (prime) by base currency. */
255
+ /**
256
+ * Aggregate assets (prime) by base currency.
257
+ * `assetQuoteType`(ETH / RTH / OVERNIGHT) 选择资产行情口径。
258
+ */
210
259
  async getAggregateAssets(req) {
211
260
  return this.callInto('aggregate_assets', withAccount(req, this.account));
212
261
  }
@@ -1 +1 @@
1
- export const SDK_VERSION = '0.4.8';
1
+ export const SDK_VERSION = '0.5.7';
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Request authentication interfaces.
3
+ *
4
+ * Implementations attach credentials, handle authentication retries, and release
5
+ * resources. OAuth 2.0 authorization flows live in `OAuth2SessionManager`; this module
6
+ * is the data plane and knows nothing about login.
7
+ */
8
+ /** Which kind of authentication. */
9
+ export declare enum AuthenticationType {
10
+ /** tigerId + RSA private key signature */
11
+ Signature = "SIGNATURE",
12
+ /** `Authorization: Bearer <access_token>` */
13
+ OAuth2 = "OAUTH2"
14
+ }
15
+ /**
16
+ * Per-request authentication context.
17
+ *
18
+ * Business parameters (`method`, `version`, `biz_content`, `timestamp`) are filled in by
19
+ * the client and must not be modified by an authentication implementation.
20
+ */
21
+ export declare class RequestAuthContext {
22
+ /** Request body parameters. Read-only as far as authentication is concerned. */
23
+ readonly params: Record<string, string>;
24
+ /**
25
+ * Whether a request may be retried with a new credential after a 401.
26
+ *
27
+ * Order placement, modification, and cancellation are not retryable because a 401
28
+ * does not prove that the operation was not executed.
29
+ */
30
+ readonly retryable: boolean;
31
+ /** Full `Authorization` header value; undefined means do not send the header. */
32
+ authorizationHeader?: string;
33
+ constructor(params: Record<string, string>, retryable?: boolean);
34
+ }
35
+ /**
36
+ * Credential snapshot used to coordinate authentication retries.
37
+ *
38
+ * Comparing the failed credential with current state detects rotation completed
39
+ * elsewhere and avoids duplicate refresh requests.
40
+ */
41
+ export declare class AuthenticationAttempt {
42
+ readonly authType: AuthenticationType;
43
+ /**
44
+ * Credential used by the request, including a raw OAuth 2.0 access token.
45
+ *
46
+ * This value is restricted to equality comparisons and must not be logged or exposed.
47
+ */
48
+ readonly credential: string | undefined;
49
+ constructor(authType: AuthenticationType, credential: string | undefined);
50
+ /** Never prints the credential value. */
51
+ toString(): string;
52
+ }
53
+ /**
54
+ * Authentication retry decision.
55
+ *
56
+ * A retry always supplies a new full `Authorization` header value, including the
57
+ * `Bearer ` prefix. There is deliberately no "retry with the same credential" option:
58
+ * that only collects the same 401 twice.
59
+ */
60
+ export declare class RetryDecision {
61
+ private static readonly NO_RETRY;
62
+ readonly shouldRetry: boolean;
63
+ readonly authorizationHeader: string | undefined;
64
+ private constructor();
65
+ static noRetry(): RetryDecision;
66
+ /** @param authorizationHeader full new header value, including `Bearer ` */
67
+ static retry(authorizationHeader: string): RetryDecision;
68
+ /** Never prints the header value. */
69
+ toString(): string;
70
+ }
71
+ /** Authentication interface. */
72
+ export interface Authentication {
73
+ /**
74
+ * Which kind of authentication. For logging and assertions; business logic should not
75
+ * branch on it.
76
+ */
77
+ readonly authType: AuthenticationType;
78
+ /**
79
+ * Write the credential into the request and return a snapshot of which credential was
80
+ * used.
81
+ *
82
+ * The OAuth2 implementation may refresh here, so this may reject with an
83
+ * `OAuth2Error`.
84
+ *
85
+ * @returns snapshot, passed back to {@link onUnauthorized}
86
+ */
87
+ apply(context: RequestAuthContext): Promise<AuthenticationAttempt>;
88
+ /**
89
+ * Return the retry decision for an unauthorized response.
90
+ *
91
+ * Refresh applies only to HTTP 401 credential failures, never to HTTP 403, server
92
+ * errors, or timeouts. Authentication retries occur at most once per request and
93
+ * require a different credential.
94
+ */
95
+ onUnauthorized(attempt: AuthenticationAttempt | undefined, statusCode: number): Promise<RetryDecision>;
96
+ /** Release resources. Must be called when switching authentication method. */
97
+ close(): void;
98
+ }
@@ -0,0 +1,4 @@
1
+ /** Authentication layer: the request-level data plane plus the OAuth 2.0 control plane. */
2
+ export { AuthenticationAttempt, AuthenticationType, RequestAuthContext, RetryDecision, } from './authentication';
3
+ export type { Authentication } from './authentication';
4
+ export * from './oauth2';
@@ -0,0 +1,32 @@
1
+ /**
2
+ * OAuth 2.0 dynamic client registration state (RFC 7591).
3
+ *
4
+ * Registration state is stored separately from tokens and retained across logout.
5
+ * `registeredScopes` records the granted scope set; scope expansion requires a new
6
+ * registration to avoid `invalid_scope`.
7
+ */
8
+ /** Raw on-disk shape, shared with the Java and Python SDKs. */
9
+ export interface OAuth2ClientRegistrationJson {
10
+ issuer?: string;
11
+ client_id?: string;
12
+ client_name?: string;
13
+ registered_scopes?: string;
14
+ client_id_issued_at?: number;
15
+ }
16
+ export declare class OAuth2ClientRegistration {
17
+ issuer?: string;
18
+ clientId?: string;
19
+ clientName?: string;
20
+ registeredScopes?: string;
21
+ clientIdIssuedAt: number;
22
+ constructor(init?: {
23
+ issuer?: string;
24
+ clientId?: string;
25
+ clientName?: string;
26
+ registeredScopes?: string;
27
+ clientIdIssuedAt?: number;
28
+ });
29
+ toJSON(): OAuth2ClientRegistrationJson;
30
+ static fromJSON(data: OAuth2ClientRegistrationJson | null | undefined): OAuth2ClientRegistration | null;
31
+ toString(): string;
32
+ }
@@ -0,0 +1,40 @@
1
+ /** Device authorization response (RFC 8628). */
2
+ /**
3
+ * Default polling interval per RFC 8628 §3.5. Tiger's authorization server does not
4
+ * return `interval`, so this default applies in practice.
5
+ */
6
+ export declare const DEFAULT_DEVICE_INTERVAL_SECONDS = 5;
7
+ /** Raw shape of the device authorization response. */
8
+ export interface OAuth2DeviceAuthorizationJson {
9
+ device_code?: string;
10
+ user_code?: string;
11
+ verification_uri?: string;
12
+ verification_uri_complete?: string;
13
+ expires_in?: number;
14
+ interval?: number;
15
+ }
16
+ export declare class OAuth2DeviceAuthorization {
17
+ /** Credential used to redeem the token; it must not be displayed or logged. */
18
+ deviceCode?: string;
19
+ /** Short code presented to the resource owner. */
20
+ userCode?: string;
21
+ /** Verification URI presented to the resource owner. */
22
+ verificationUri?: string;
23
+ /** Verification URI with the user code included. */
24
+ verificationUriComplete?: string;
25
+ expiresIn: number;
26
+ /** Polling interval in seconds; 0 means the server sent none */
27
+ interval: number;
28
+ constructor(init?: {
29
+ deviceCode?: string;
30
+ userCode?: string;
31
+ verificationUri?: string;
32
+ verificationUriComplete?: string;
33
+ expiresIn?: number;
34
+ interval?: number;
35
+ });
36
+ effectiveIntervalSeconds(): number;
37
+ static fromJSON(data: OAuth2DeviceAuthorizationJson | null | undefined): OAuth2DeviceAuthorization | null;
38
+ /** Prints no deviceCode. */
39
+ toString(): string;
40
+ }
@@ -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
+ }