@pinta365/strava 0.0.1
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/LICENSE +21 -0
- package/README.md +390 -0
- package/esm/_dnt.shims.d.ts +2 -0
- package/esm/_dnt.shims.js +57 -0
- package/esm/deps/jsr.io/@cross/runtime/1.2.1/mod.d.ts +126 -0
- package/esm/deps/jsr.io/@cross/runtime/1.2.1/mod.js +480 -0
- package/esm/mod.d.ts +27 -0
- package/esm/mod.js +27 -0
- package/esm/package.json +3 -0
- package/esm/src/auth/oauth.d.ts +68 -0
- package/esm/src/auth/oauth.js +203 -0
- package/esm/src/auth/scopes.d.ts +52 -0
- package/esm/src/auth/scopes.js +71 -0
- package/esm/src/auth/token-store.d.ts +57 -0
- package/esm/src/auth/token-store.js +142 -0
- package/esm/src/client.d.ts +98 -0
- package/esm/src/client.js +235 -0
- package/esm/src/errors.d.ts +52 -0
- package/esm/src/errors.js +102 -0
- package/esm/src/http/deduplication.d.ts +33 -0
- package/esm/src/http/deduplication.js +96 -0
- package/esm/src/http/rate-limiter.d.ts +47 -0
- package/esm/src/http/rate-limiter.js +168 -0
- package/esm/src/http/request.d.ts +24 -0
- package/esm/src/http/request.js +158 -0
- package/esm/src/http/retry.d.ts +9 -0
- package/esm/src/http/retry.js +61 -0
- package/esm/src/resources/activities.d.ts +149 -0
- package/esm/src/resources/activities.js +189 -0
- package/esm/src/resources/athletes.d.ts +37 -0
- package/esm/src/resources/athletes.js +85 -0
- package/esm/src/resources/clubs.d.ts +45 -0
- package/esm/src/resources/clubs.js +71 -0
- package/esm/src/resources/gears.d.ts +17 -0
- package/esm/src/resources/gears.js +27 -0
- package/esm/src/resources/routes.d.ts +33 -0
- package/esm/src/resources/routes.js +71 -0
- package/esm/src/resources/segment-efforts.d.ts +38 -0
- package/esm/src/resources/segment-efforts.js +53 -0
- package/esm/src/resources/segments.d.ts +42 -0
- package/esm/src/resources/segments.js +67 -0
- package/esm/src/resources/streams.d.ts +44 -0
- package/esm/src/resources/streams.js +75 -0
- package/esm/src/resources/uploads.d.ts +41 -0
- package/esm/src/resources/uploads.js +79 -0
- package/esm/src/types/api.d.ts +9 -0
- package/esm/src/types/api.js +7 -0
- package/esm/src/types/common.d.ts +65 -0
- package/esm/src/types/common.js +4 -0
- package/esm/src/types/generated.d.ts +731 -0
- package/esm/src/types/generated.js +7 -0
- package/esm/src/utils/pagination.d.ts +45 -0
- package/esm/src/utils/pagination.js +112 -0
- package/esm/src/utils/transformers.d.ts +30 -0
- package/esm/src/utils/transformers.js +189 -0
- package/esm/src/utils/validators.d.ts +53 -0
- package/esm/src/utils/validators.js +84 -0
- package/package.json +40 -0
- package/script/_dnt.shims.d.ts +2 -0
- package/script/_dnt.shims.js +60 -0
- package/script/deps/jsr.io/@cross/runtime/1.2.1/mod.d.ts +126 -0
- package/script/deps/jsr.io/@cross/runtime/1.2.1/mod.js +526 -0
- package/script/mod.d.ts +27 -0
- package/script/mod.js +73 -0
- package/script/package.json +3 -0
- package/script/src/auth/oauth.d.ts +68 -0
- package/script/src/auth/oauth.js +211 -0
- package/script/src/auth/scopes.d.ts +52 -0
- package/script/src/auth/scopes.js +79 -0
- package/script/src/auth/token-store.d.ts +57 -0
- package/script/src/auth/token-store.js +182 -0
- package/script/src/client.d.ts +98 -0
- package/script/src/client.js +239 -0
- package/script/src/errors.d.ts +52 -0
- package/script/src/errors.js +111 -0
- package/script/src/http/deduplication.d.ts +33 -0
- package/script/src/http/deduplication.js +100 -0
- package/script/src/http/rate-limiter.d.ts +47 -0
- package/script/src/http/rate-limiter.js +172 -0
- package/script/src/http/request.d.ts +24 -0
- package/script/src/http/request.js +161 -0
- package/script/src/http/retry.d.ts +9 -0
- package/script/src/http/retry.js +64 -0
- package/script/src/resources/activities.d.ts +149 -0
- package/script/src/resources/activities.js +193 -0
- package/script/src/resources/athletes.d.ts +37 -0
- package/script/src/resources/athletes.js +89 -0
- package/script/src/resources/clubs.d.ts +45 -0
- package/script/src/resources/clubs.js +75 -0
- package/script/src/resources/gears.d.ts +17 -0
- package/script/src/resources/gears.js +31 -0
- package/script/src/resources/routes.d.ts +33 -0
- package/script/src/resources/routes.js +75 -0
- package/script/src/resources/segment-efforts.d.ts +38 -0
- package/script/src/resources/segment-efforts.js +57 -0
- package/script/src/resources/segments.d.ts +42 -0
- package/script/src/resources/segments.js +71 -0
- package/script/src/resources/streams.d.ts +44 -0
- package/script/src/resources/streams.js +79 -0
- package/script/src/resources/uploads.d.ts +41 -0
- package/script/src/resources/uploads.js +83 -0
- package/script/src/types/api.d.ts +9 -0
- package/script/src/types/api.js +23 -0
- package/script/src/types/common.d.ts +65 -0
- package/script/src/types/common.js +5 -0
- package/script/src/types/generated.d.ts +731 -0
- package/script/src/types/generated.js +8 -0
- package/script/src/utils/pagination.d.ts +45 -0
- package/script/src/utils/pagination.js +118 -0
- package/script/src/utils/transformers.d.ts +30 -0
- package/script/src/utils/transformers.js +196 -0
- package/script/src/utils/validators.d.ts +53 -0
- package/script/src/utils/validators.js +92 -0
package/script/mod.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @pinta365/strava - TypeScript client for Strava API v3
|
|
4
|
+
*
|
|
5
|
+
* A cross-runtime (Deno, Node.js, Bun, Browser) library for interacting with the Strava API.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.validateRange = exports.validatePositiveInteger = exports.validatePagination = exports.validateNonNegativeInteger = exports.validateNonEmptyString = exports.validateDate = exports.transformDates = exports.flattenResponses = exports.applyTransformations = exports.addComputedFields = exports.PaginatedIterator = exports.listAll = exports.buildPaginationQuery = exports.StreamsResource = exports.UploadsResource = exports.RoutesResource = exports.GearsResource = exports.ClubsResource = exports.SegmentEffortsResource = exports.SegmentsResource = exports.ActivitiesResource = exports.AthletesResource = exports.MemoryTokenStore = exports.LocalStorageTokenStore = exports.getDefaultTokenStore = exports.FileSystemTokenStore = exports.validateScopes = exports.StravaScope = exports.parseScopes = exports.hasScope = exports.hasAllScopes = exports.formatScopes = exports.refreshAccessToken = exports.OAuthManager = exports.getAuthorizationUrl = exports.exchangeCode = exports.StravaValidationError = exports.StravaServerError = exports.StravaRateLimitError = exports.StravaNotFoundError = exports.StravaError = exports.StravaAuthError = exports.StravaClient = void 0;
|
|
9
|
+
// Main client
|
|
10
|
+
var client_js_1 = require("./src/client.js");
|
|
11
|
+
Object.defineProperty(exports, "StravaClient", { enumerable: true, get: function () { return client_js_1.StravaClient; } });
|
|
12
|
+
// Error classes
|
|
13
|
+
var errors_js_1 = require("./src/errors.js");
|
|
14
|
+
Object.defineProperty(exports, "StravaAuthError", { enumerable: true, get: function () { return errors_js_1.StravaAuthError; } });
|
|
15
|
+
Object.defineProperty(exports, "StravaError", { enumerable: true, get: function () { return errors_js_1.StravaError; } });
|
|
16
|
+
Object.defineProperty(exports, "StravaNotFoundError", { enumerable: true, get: function () { return errors_js_1.StravaNotFoundError; } });
|
|
17
|
+
Object.defineProperty(exports, "StravaRateLimitError", { enumerable: true, get: function () { return errors_js_1.StravaRateLimitError; } });
|
|
18
|
+
Object.defineProperty(exports, "StravaServerError", { enumerable: true, get: function () { return errors_js_1.StravaServerError; } });
|
|
19
|
+
Object.defineProperty(exports, "StravaValidationError", { enumerable: true, get: function () { return errors_js_1.StravaValidationError; } });
|
|
20
|
+
// Auth
|
|
21
|
+
var oauth_js_1 = require("./src/auth/oauth.js");
|
|
22
|
+
Object.defineProperty(exports, "exchangeCode", { enumerable: true, get: function () { return oauth_js_1.exchangeCode; } });
|
|
23
|
+
Object.defineProperty(exports, "getAuthorizationUrl", { enumerable: true, get: function () { return oauth_js_1.getAuthorizationUrl; } });
|
|
24
|
+
Object.defineProperty(exports, "OAuthManager", { enumerable: true, get: function () { return oauth_js_1.OAuthManager; } });
|
|
25
|
+
Object.defineProperty(exports, "refreshAccessToken", { enumerable: true, get: function () { return oauth_js_1.refreshAccessToken; } });
|
|
26
|
+
var scopes_js_1 = require("./src/auth/scopes.js");
|
|
27
|
+
Object.defineProperty(exports, "formatScopes", { enumerable: true, get: function () { return scopes_js_1.formatScopes; } });
|
|
28
|
+
Object.defineProperty(exports, "hasAllScopes", { enumerable: true, get: function () { return scopes_js_1.hasAllScopes; } });
|
|
29
|
+
Object.defineProperty(exports, "hasScope", { enumerable: true, get: function () { return scopes_js_1.hasScope; } });
|
|
30
|
+
Object.defineProperty(exports, "parseScopes", { enumerable: true, get: function () { return scopes_js_1.parseScopes; } });
|
|
31
|
+
Object.defineProperty(exports, "StravaScope", { enumerable: true, get: function () { return scopes_js_1.StravaScope; } });
|
|
32
|
+
Object.defineProperty(exports, "validateScopes", { enumerable: true, get: function () { return scopes_js_1.validateScopes; } });
|
|
33
|
+
var token_store_js_1 = require("./src/auth/token-store.js");
|
|
34
|
+
Object.defineProperty(exports, "FileSystemTokenStore", { enumerable: true, get: function () { return token_store_js_1.FileSystemTokenStore; } });
|
|
35
|
+
Object.defineProperty(exports, "getDefaultTokenStore", { enumerable: true, get: function () { return token_store_js_1.getDefaultTokenStore; } });
|
|
36
|
+
Object.defineProperty(exports, "LocalStorageTokenStore", { enumerable: true, get: function () { return token_store_js_1.LocalStorageTokenStore; } });
|
|
37
|
+
Object.defineProperty(exports, "MemoryTokenStore", { enumerable: true, get: function () { return token_store_js_1.MemoryTokenStore; } });
|
|
38
|
+
// Resources (for advanced usage)
|
|
39
|
+
var athletes_js_1 = require("./src/resources/athletes.js");
|
|
40
|
+
Object.defineProperty(exports, "AthletesResource", { enumerable: true, get: function () { return athletes_js_1.AthletesResource; } });
|
|
41
|
+
var activities_js_1 = require("./src/resources/activities.js");
|
|
42
|
+
Object.defineProperty(exports, "ActivitiesResource", { enumerable: true, get: function () { return activities_js_1.ActivitiesResource; } });
|
|
43
|
+
var segments_js_1 = require("./src/resources/segments.js");
|
|
44
|
+
Object.defineProperty(exports, "SegmentsResource", { enumerable: true, get: function () { return segments_js_1.SegmentsResource; } });
|
|
45
|
+
var segment_efforts_js_1 = require("./src/resources/segment-efforts.js");
|
|
46
|
+
Object.defineProperty(exports, "SegmentEffortsResource", { enumerable: true, get: function () { return segment_efforts_js_1.SegmentEffortsResource; } });
|
|
47
|
+
var clubs_js_1 = require("./src/resources/clubs.js");
|
|
48
|
+
Object.defineProperty(exports, "ClubsResource", { enumerable: true, get: function () { return clubs_js_1.ClubsResource; } });
|
|
49
|
+
var gears_js_1 = require("./src/resources/gears.js");
|
|
50
|
+
Object.defineProperty(exports, "GearsResource", { enumerable: true, get: function () { return gears_js_1.GearsResource; } });
|
|
51
|
+
var routes_js_1 = require("./src/resources/routes.js");
|
|
52
|
+
Object.defineProperty(exports, "RoutesResource", { enumerable: true, get: function () { return routes_js_1.RoutesResource; } });
|
|
53
|
+
var uploads_js_1 = require("./src/resources/uploads.js");
|
|
54
|
+
Object.defineProperty(exports, "UploadsResource", { enumerable: true, get: function () { return uploads_js_1.UploadsResource; } });
|
|
55
|
+
var streams_js_1 = require("./src/resources/streams.js");
|
|
56
|
+
Object.defineProperty(exports, "StreamsResource", { enumerable: true, get: function () { return streams_js_1.StreamsResource; } });
|
|
57
|
+
// Utilities
|
|
58
|
+
var pagination_js_1 = require("./src/utils/pagination.js");
|
|
59
|
+
Object.defineProperty(exports, "buildPaginationQuery", { enumerable: true, get: function () { return pagination_js_1.buildPaginationQuery; } });
|
|
60
|
+
Object.defineProperty(exports, "listAll", { enumerable: true, get: function () { return pagination_js_1.listAll; } });
|
|
61
|
+
Object.defineProperty(exports, "PaginatedIterator", { enumerable: true, get: function () { return pagination_js_1.PaginatedIterator; } });
|
|
62
|
+
var transformers_js_1 = require("./src/utils/transformers.js");
|
|
63
|
+
Object.defineProperty(exports, "addComputedFields", { enumerable: true, get: function () { return transformers_js_1.addComputedFields; } });
|
|
64
|
+
Object.defineProperty(exports, "applyTransformations", { enumerable: true, get: function () { return transformers_js_1.applyTransformations; } });
|
|
65
|
+
Object.defineProperty(exports, "flattenResponses", { enumerable: true, get: function () { return transformers_js_1.flattenResponses; } });
|
|
66
|
+
Object.defineProperty(exports, "transformDates", { enumerable: true, get: function () { return transformers_js_1.transformDates; } });
|
|
67
|
+
var validators_js_1 = require("./src/utils/validators.js");
|
|
68
|
+
Object.defineProperty(exports, "validateDate", { enumerable: true, get: function () { return validators_js_1.validateDate; } });
|
|
69
|
+
Object.defineProperty(exports, "validateNonEmptyString", { enumerable: true, get: function () { return validators_js_1.validateNonEmptyString; } });
|
|
70
|
+
Object.defineProperty(exports, "validateNonNegativeInteger", { enumerable: true, get: function () { return validators_js_1.validateNonNegativeInteger; } });
|
|
71
|
+
Object.defineProperty(exports, "validatePagination", { enumerable: true, get: function () { return validators_js_1.validatePagination; } });
|
|
72
|
+
Object.defineProperty(exports, "validatePositiveInteger", { enumerable: true, get: function () { return validators_js_1.validatePositiveInteger; } });
|
|
73
|
+
Object.defineProperty(exports, "validateRange", { enumerable: true, get: function () { return validators_js_1.validateRange; } });
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth 2.0 authentication flow for Strava API
|
|
3
|
+
*/
|
|
4
|
+
import type { TokenData, TokenStore } from "./token-store.js";
|
|
5
|
+
import { type StravaScope } from "./scopes.js";
|
|
6
|
+
interface AuthorizationUrlOptions {
|
|
7
|
+
clientId: string;
|
|
8
|
+
redirectUri: string;
|
|
9
|
+
scope: StravaScope[];
|
|
10
|
+
state?: string;
|
|
11
|
+
approvalPrompt?: "force" | "auto";
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Generate authorization URL
|
|
15
|
+
*/
|
|
16
|
+
export declare function getAuthorizationUrl(options: AuthorizationUrlOptions): string;
|
|
17
|
+
/**
|
|
18
|
+
* Exchange authorization code for tokens
|
|
19
|
+
* @param code - Authorization code from OAuth callback
|
|
20
|
+
* @param clientId - Strava client ID
|
|
21
|
+
* @param clientSecret - Strava client secret
|
|
22
|
+
* @param redirectUri - Redirect URI used in authorization (optional)
|
|
23
|
+
* @returns Token data
|
|
24
|
+
*/
|
|
25
|
+
export declare function exchangeCode(code: string, clientId: string, clientSecret: string, redirectUri?: string): Promise<TokenData>;
|
|
26
|
+
/**
|
|
27
|
+
* Refresh access token using refresh token
|
|
28
|
+
*/
|
|
29
|
+
export declare function refreshAccessToken(refreshToken: string, clientId: string, clientSecret: string): Promise<TokenData>;
|
|
30
|
+
/**
|
|
31
|
+
* Check if token is expired or about to expire
|
|
32
|
+
*/
|
|
33
|
+
export declare function isTokenExpired(token: TokenData, bufferSeconds?: number): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* OAuth manager for handling authentication and token refresh
|
|
36
|
+
*/
|
|
37
|
+
export declare class OAuthManager {
|
|
38
|
+
private tokenStore;
|
|
39
|
+
private clientId;
|
|
40
|
+
private clientSecret;
|
|
41
|
+
constructor(clientId: string, clientSecret: string, tokenStore: TokenStore);
|
|
42
|
+
/**
|
|
43
|
+
* Get authorization URL
|
|
44
|
+
*/
|
|
45
|
+
getAuthorizationUrl(options: Omit<AuthorizationUrlOptions, "clientId">): string;
|
|
46
|
+
/**
|
|
47
|
+
* Exchange code and store tokens
|
|
48
|
+
*/
|
|
49
|
+
authenticate(code: string, redirectUri?: string): Promise<TokenData>;
|
|
50
|
+
/**
|
|
51
|
+
* Get current token, refreshing if needed
|
|
52
|
+
*/
|
|
53
|
+
getToken(): Promise<TokenData | null>;
|
|
54
|
+
/**
|
|
55
|
+
* Manually refresh token
|
|
56
|
+
*/
|
|
57
|
+
refreshToken(): Promise<TokenData>;
|
|
58
|
+
/**
|
|
59
|
+
* Get current scopes
|
|
60
|
+
*/
|
|
61
|
+
getScopes(): Promise<StravaScope[]>;
|
|
62
|
+
/**
|
|
63
|
+
* Clear stored tokens
|
|
64
|
+
*/
|
|
65
|
+
clearTokens(): Promise<void>;
|
|
66
|
+
}
|
|
67
|
+
export {};
|
|
68
|
+
//# sourceMappingURL=oauth.d.ts.map
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OAuth 2.0 authentication flow for Strava API
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.OAuthManager = void 0;
|
|
7
|
+
exports.getAuthorizationUrl = getAuthorizationUrl;
|
|
8
|
+
exports.exchangeCode = exchangeCode;
|
|
9
|
+
exports.refreshAccessToken = refreshAccessToken;
|
|
10
|
+
exports.isTokenExpired = isTokenExpired;
|
|
11
|
+
const errors_js_1 = require("../errors.js");
|
|
12
|
+
const scopes_js_1 = require("./scopes.js");
|
|
13
|
+
const OAUTH_BASE_URL = "https://www.strava.com/api/v3/oauth";
|
|
14
|
+
/**
|
|
15
|
+
* Generate authorization URL
|
|
16
|
+
*/
|
|
17
|
+
function getAuthorizationUrl(options) {
|
|
18
|
+
const { clientId, redirectUri, scope, state, approvalPrompt = "auto" } = options;
|
|
19
|
+
const params = new URLSearchParams({
|
|
20
|
+
client_id: clientId,
|
|
21
|
+
redirect_uri: redirectUri,
|
|
22
|
+
response_type: "code",
|
|
23
|
+
scope: (0, scopes_js_1.formatScopes)(scope),
|
|
24
|
+
approval_prompt: approvalPrompt,
|
|
25
|
+
});
|
|
26
|
+
if (state) {
|
|
27
|
+
params.append("state", state);
|
|
28
|
+
}
|
|
29
|
+
return `${OAUTH_BASE_URL}/authorize?${params.toString()}`;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Exchange authorization code for tokens
|
|
33
|
+
* @param code - Authorization code from OAuth callback
|
|
34
|
+
* @param clientId - Strava client ID
|
|
35
|
+
* @param clientSecret - Strava client secret
|
|
36
|
+
* @param redirectUri - Redirect URI used in authorization (optional)
|
|
37
|
+
* @returns Token data
|
|
38
|
+
*/
|
|
39
|
+
async function exchangeCode(code, clientId, clientSecret, redirectUri) {
|
|
40
|
+
const params = new URLSearchParams({
|
|
41
|
+
client_id: clientId,
|
|
42
|
+
client_secret: clientSecret,
|
|
43
|
+
code,
|
|
44
|
+
grant_type: "authorization_code",
|
|
45
|
+
});
|
|
46
|
+
if (redirectUri) {
|
|
47
|
+
params.append("redirect_uri", redirectUri);
|
|
48
|
+
}
|
|
49
|
+
const response = await fetch(`${OAUTH_BASE_URL}/token`, {
|
|
50
|
+
method: "POST",
|
|
51
|
+
headers: {
|
|
52
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
53
|
+
},
|
|
54
|
+
body: params.toString(),
|
|
55
|
+
});
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
const error = await response.json().catch(() => ({ message: response.statusText }));
|
|
58
|
+
throw new errors_js_1.StravaAuthError(`Failed to exchange code: ${error.message || response.statusText}`, response.status, error);
|
|
59
|
+
}
|
|
60
|
+
const data = await response.json();
|
|
61
|
+
return {
|
|
62
|
+
accessToken: data.access_token,
|
|
63
|
+
refreshToken: data.refresh_token,
|
|
64
|
+
expiresAt: data.expires_at,
|
|
65
|
+
tokenType: data.token_type,
|
|
66
|
+
athleteId: data.athlete?.id,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Refresh access token using refresh token
|
|
71
|
+
*/
|
|
72
|
+
async function refreshAccessToken(refreshToken, clientId, clientSecret) {
|
|
73
|
+
const params = new URLSearchParams({
|
|
74
|
+
client_id: clientId,
|
|
75
|
+
client_secret: clientSecret,
|
|
76
|
+
refresh_token: refreshToken,
|
|
77
|
+
grant_type: "refresh_token",
|
|
78
|
+
});
|
|
79
|
+
const response = await fetch(`${OAUTH_BASE_URL}/token`, {
|
|
80
|
+
method: "POST",
|
|
81
|
+
headers: {
|
|
82
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
83
|
+
},
|
|
84
|
+
body: params.toString(),
|
|
85
|
+
});
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
const error = await response.json().catch(() => ({ message: response.statusText }));
|
|
88
|
+
throw new errors_js_1.StravaAuthError(`Failed to refresh token: ${error.message || response.statusText}`, response.status, error);
|
|
89
|
+
}
|
|
90
|
+
const data = await response.json();
|
|
91
|
+
return {
|
|
92
|
+
accessToken: data.access_token,
|
|
93
|
+
refreshToken: data.refresh_token,
|
|
94
|
+
expiresAt: data.expires_at,
|
|
95
|
+
tokenType: data.token_type,
|
|
96
|
+
athleteId: data.athlete?.id,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Check if token is expired or about to expire
|
|
101
|
+
*/
|
|
102
|
+
function isTokenExpired(token, bufferSeconds = 300) {
|
|
103
|
+
const now = Math.floor(Date.now() / 1000);
|
|
104
|
+
return token.expiresAt <= (now + bufferSeconds);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* OAuth manager for handling authentication and token refresh
|
|
108
|
+
*/
|
|
109
|
+
class OAuthManager {
|
|
110
|
+
constructor(clientId, clientSecret, tokenStore) {
|
|
111
|
+
Object.defineProperty(this, "tokenStore", {
|
|
112
|
+
enumerable: true,
|
|
113
|
+
configurable: true,
|
|
114
|
+
writable: true,
|
|
115
|
+
value: void 0
|
|
116
|
+
});
|
|
117
|
+
Object.defineProperty(this, "clientId", {
|
|
118
|
+
enumerable: true,
|
|
119
|
+
configurable: true,
|
|
120
|
+
writable: true,
|
|
121
|
+
value: void 0
|
|
122
|
+
});
|
|
123
|
+
Object.defineProperty(this, "clientSecret", {
|
|
124
|
+
enumerable: true,
|
|
125
|
+
configurable: true,
|
|
126
|
+
writable: true,
|
|
127
|
+
value: void 0
|
|
128
|
+
});
|
|
129
|
+
this.clientId = clientId;
|
|
130
|
+
this.clientSecret = clientSecret;
|
|
131
|
+
this.tokenStore = tokenStore;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get authorization URL
|
|
135
|
+
*/
|
|
136
|
+
getAuthorizationUrl(options) {
|
|
137
|
+
return getAuthorizationUrl({
|
|
138
|
+
...options,
|
|
139
|
+
clientId: this.clientId,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Exchange code and store tokens
|
|
144
|
+
*/
|
|
145
|
+
async authenticate(code, redirectUri) {
|
|
146
|
+
const token = await exchangeCode(code, this.clientId, this.clientSecret, redirectUri);
|
|
147
|
+
await this.tokenStore.set(token);
|
|
148
|
+
return token;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get current token, refreshing if needed
|
|
152
|
+
*/
|
|
153
|
+
async getToken() {
|
|
154
|
+
const token = await this.tokenStore.get();
|
|
155
|
+
if (!token) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
// Refresh if expired or about to expire
|
|
159
|
+
if (isTokenExpired(token)) {
|
|
160
|
+
try {
|
|
161
|
+
const newToken = await refreshAccessToken(token.refreshToken, this.clientId, this.clientSecret);
|
|
162
|
+
const refreshedToken = {
|
|
163
|
+
...newToken,
|
|
164
|
+
scope: token.scope,
|
|
165
|
+
athleteId: token.athleteId || newToken.athleteId,
|
|
166
|
+
};
|
|
167
|
+
await this.tokenStore.set(refreshedToken);
|
|
168
|
+
return refreshedToken;
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
await this.tokenStore.clear();
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return token;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Manually refresh token
|
|
179
|
+
*/
|
|
180
|
+
async refreshToken() {
|
|
181
|
+
const token = await this.tokenStore.get();
|
|
182
|
+
if (!token) {
|
|
183
|
+
throw new errors_js_1.StravaAuthError("No token available to refresh");
|
|
184
|
+
}
|
|
185
|
+
const newToken = await refreshAccessToken(token.refreshToken, this.clientId, this.clientSecret);
|
|
186
|
+
const refreshedToken = {
|
|
187
|
+
...newToken,
|
|
188
|
+
scope: token.scope,
|
|
189
|
+
athleteId: token.athleteId || newToken.athleteId,
|
|
190
|
+
};
|
|
191
|
+
await this.tokenStore.set(refreshedToken);
|
|
192
|
+
return refreshedToken;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Get current scopes
|
|
196
|
+
*/
|
|
197
|
+
async getScopes() {
|
|
198
|
+
const token = await this.tokenStore.get();
|
|
199
|
+
if (!token || !token.scope) {
|
|
200
|
+
return [];
|
|
201
|
+
}
|
|
202
|
+
return (0, scopes_js_1.parseScopes)(token.scope);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Clear stored tokens
|
|
206
|
+
*/
|
|
207
|
+
async clearTokens() {
|
|
208
|
+
await this.tokenStore.clear();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
exports.OAuthManager = OAuthManager;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth 2.0 scope definitions for Strava API
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Available OAuth scopes
|
|
6
|
+
*/
|
|
7
|
+
export declare enum StravaScope {
|
|
8
|
+
Read = "read",
|
|
9
|
+
ReadAll = "read_all",
|
|
10
|
+
ProfileReadAll = "profile:read_all",
|
|
11
|
+
ProfileWrite = "profile:write",
|
|
12
|
+
ActivityRead = "activity:read",
|
|
13
|
+
ActivityReadAll = "activity:read_all",
|
|
14
|
+
ActivityWrite = "activity:write"
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Scope descriptions
|
|
18
|
+
*/
|
|
19
|
+
export declare const SCOPE_DESCRIPTIONS: Record<StravaScope, string>;
|
|
20
|
+
/**
|
|
21
|
+
* Parse scope string into array
|
|
22
|
+
* @param scopeString - Comma-separated scope string
|
|
23
|
+
* @returns Array of scope enums
|
|
24
|
+
*/
|
|
25
|
+
export declare function parseScopes(scopeString: string): StravaScope[];
|
|
26
|
+
/**
|
|
27
|
+
* Format scopes array into string
|
|
28
|
+
* @param scopes - Array of scope enums
|
|
29
|
+
* @returns Comma-separated scope string
|
|
30
|
+
*/
|
|
31
|
+
export declare function formatScopes(scopes: StravaScope[]): string;
|
|
32
|
+
/**
|
|
33
|
+
* Check if a scope is included in the provided scopes
|
|
34
|
+
* @param required - Required scope
|
|
35
|
+
* @param available - Available scopes
|
|
36
|
+
* @returns True if the required scope is available
|
|
37
|
+
*/
|
|
38
|
+
export declare function hasScope(required: StravaScope, available: StravaScope[]): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Check if all required scopes are available
|
|
41
|
+
* @param required - Required scopes
|
|
42
|
+
* @param available - Available scopes
|
|
43
|
+
* @returns True if all required scopes are available
|
|
44
|
+
*/
|
|
45
|
+
export declare function hasAllScopes(required: StravaScope[], available: StravaScope[]): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Validate scopes
|
|
48
|
+
* @param scopes - Scopes to validate
|
|
49
|
+
* @returns True if all scopes are valid
|
|
50
|
+
*/
|
|
51
|
+
export declare function validateScopes(scopes: StravaScope[]): boolean;
|
|
52
|
+
//# sourceMappingURL=scopes.d.ts.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OAuth 2.0 scope definitions for Strava API
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SCOPE_DESCRIPTIONS = exports.StravaScope = void 0;
|
|
7
|
+
exports.parseScopes = parseScopes;
|
|
8
|
+
exports.formatScopes = formatScopes;
|
|
9
|
+
exports.hasScope = hasScope;
|
|
10
|
+
exports.hasAllScopes = hasAllScopes;
|
|
11
|
+
exports.validateScopes = validateScopes;
|
|
12
|
+
/**
|
|
13
|
+
* Available OAuth scopes
|
|
14
|
+
*/
|
|
15
|
+
var StravaScope;
|
|
16
|
+
(function (StravaScope) {
|
|
17
|
+
StravaScope["Read"] = "read";
|
|
18
|
+
StravaScope["ReadAll"] = "read_all";
|
|
19
|
+
StravaScope["ProfileReadAll"] = "profile:read_all";
|
|
20
|
+
StravaScope["ProfileWrite"] = "profile:write";
|
|
21
|
+
StravaScope["ActivityRead"] = "activity:read";
|
|
22
|
+
StravaScope["ActivityReadAll"] = "activity:read_all";
|
|
23
|
+
StravaScope["ActivityWrite"] = "activity:write";
|
|
24
|
+
})(StravaScope || (exports.StravaScope = StravaScope = {}));
|
|
25
|
+
/**
|
|
26
|
+
* Scope descriptions
|
|
27
|
+
*/
|
|
28
|
+
exports.SCOPE_DESCRIPTIONS = {
|
|
29
|
+
[StravaScope.Read]: "Read public segments, public routes, public profile data, public posts, public events, club feeds, and leaderboards",
|
|
30
|
+
[StravaScope.ReadAll]: "Read private routes, private segments, and private events for the user",
|
|
31
|
+
[StravaScope.ProfileReadAll]: "Read all profile information even if the user has set their profile visibility to Followers or Only You",
|
|
32
|
+
[StravaScope.ProfileWrite]: "Update the user's weight and Functional Threshold Power (FTP), and access to star or unstar segments on their behalf",
|
|
33
|
+
[StravaScope.ActivityRead]: "Read the user's activity data for activities that are visible to Everyone and Followers, excluding privacy zone data",
|
|
34
|
+
[StravaScope.ActivityReadAll]: "The same access as activity:read, plus privacy zone data and access to read the user's activities with visibility set to Only You",
|
|
35
|
+
[StravaScope.ActivityWrite]: "Access to create manual activities and uploads, and access to edit any activities that are visible to the app, based on activity read access level",
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Parse scope string into array
|
|
39
|
+
* @param scopeString - Comma-separated scope string
|
|
40
|
+
* @returns Array of scope enums
|
|
41
|
+
*/
|
|
42
|
+
function parseScopes(scopeString) {
|
|
43
|
+
return scopeString.split(",").map((s) => s.trim()).filter(Boolean);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Format scopes array into string
|
|
47
|
+
* @param scopes - Array of scope enums
|
|
48
|
+
* @returns Comma-separated scope string
|
|
49
|
+
*/
|
|
50
|
+
function formatScopes(scopes) {
|
|
51
|
+
return scopes.join(",");
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Check if a scope is included in the provided scopes
|
|
55
|
+
* @param required - Required scope
|
|
56
|
+
* @param available - Available scopes
|
|
57
|
+
* @returns True if the required scope is available
|
|
58
|
+
*/
|
|
59
|
+
function hasScope(required, available) {
|
|
60
|
+
return available.includes(required);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Check if all required scopes are available
|
|
64
|
+
* @param required - Required scopes
|
|
65
|
+
* @param available - Available scopes
|
|
66
|
+
* @returns True if all required scopes are available
|
|
67
|
+
*/
|
|
68
|
+
function hasAllScopes(required, available) {
|
|
69
|
+
return required.every((scope) => available.includes(scope));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Validate scopes
|
|
73
|
+
* @param scopes - Scopes to validate
|
|
74
|
+
* @returns True if all scopes are valid
|
|
75
|
+
*/
|
|
76
|
+
function validateScopes(scopes) {
|
|
77
|
+
const validScopes = Object.values(StravaScope);
|
|
78
|
+
return scopes.every((scope) => validScopes.includes(scope));
|
|
79
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token storage interface and implementations
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Token data structure
|
|
6
|
+
*/
|
|
7
|
+
export interface TokenData {
|
|
8
|
+
accessToken: string;
|
|
9
|
+
refreshToken: string;
|
|
10
|
+
expiresAt: number;
|
|
11
|
+
tokenType: string;
|
|
12
|
+
scope?: string;
|
|
13
|
+
athleteId?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Token storage interface
|
|
17
|
+
*/
|
|
18
|
+
export interface TokenStore {
|
|
19
|
+
get(): Promise<TokenData | null>;
|
|
20
|
+
set(token: TokenData): Promise<void>;
|
|
21
|
+
clear(): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* In-memory token store (default)
|
|
25
|
+
*/
|
|
26
|
+
export declare class MemoryTokenStore implements TokenStore {
|
|
27
|
+
private token;
|
|
28
|
+
get(): Promise<TokenData | null>;
|
|
29
|
+
set(token: TokenData): Promise<void>;
|
|
30
|
+
clear(): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Browser localStorage token store
|
|
34
|
+
*/
|
|
35
|
+
export declare class LocalStorageTokenStore implements TokenStore {
|
|
36
|
+
private readonly key;
|
|
37
|
+
constructor(key?: string);
|
|
38
|
+
get(): Promise<TokenData | null>;
|
|
39
|
+
set(token: TokenData): Promise<void>;
|
|
40
|
+
clear(): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* File system token store (Node.js/Deno)
|
|
44
|
+
*/
|
|
45
|
+
export declare class FileSystemTokenStore implements TokenStore {
|
|
46
|
+
private readonly path;
|
|
47
|
+
constructor(path?: string);
|
|
48
|
+
get(): Promise<TokenData | null>;
|
|
49
|
+
set(token: TokenData): Promise<void>;
|
|
50
|
+
clear(): Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get default token store based on runtime environment
|
|
54
|
+
* @returns TokenStore appropriate for the current runtime (browser, Node.js, Deno, Bun)
|
|
55
|
+
*/
|
|
56
|
+
export declare function getDefaultTokenStore(): TokenStore;
|
|
57
|
+
//# sourceMappingURL=token-store.d.ts.map
|