@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
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pagination helpers for Strava API
|
|
3
|
+
*/
|
|
4
|
+
import type { PaginationOptions } from "../types/common.js";
|
|
5
|
+
/**
|
|
6
|
+
* Paginated iterator for auto-pagination
|
|
7
|
+
*/
|
|
8
|
+
export declare class PaginatedIterator<T> {
|
|
9
|
+
private fetchPage;
|
|
10
|
+
private perPage;
|
|
11
|
+
private currentPage;
|
|
12
|
+
private currentItems;
|
|
13
|
+
private hasMore;
|
|
14
|
+
constructor(fetchPage: (page: number, perPage: number) => Promise<T[]>, perPage?: number);
|
|
15
|
+
/**
|
|
16
|
+
* Get next page
|
|
17
|
+
*/
|
|
18
|
+
next(): Promise<boolean>;
|
|
19
|
+
/**
|
|
20
|
+
* Get current page items
|
|
21
|
+
*/
|
|
22
|
+
get current(): T[];
|
|
23
|
+
/**
|
|
24
|
+
* Check if there are more pages
|
|
25
|
+
*/
|
|
26
|
+
get hasMorePages(): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Reset iterator
|
|
29
|
+
*/
|
|
30
|
+
reset(): void;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Create async iterator for auto-pagination
|
|
34
|
+
* @param fetchPage - Function to fetch a page of data
|
|
35
|
+
* @param perPage - Number of items per page (default: 30)
|
|
36
|
+
* @returns Async generator that yields all items across pages
|
|
37
|
+
*/
|
|
38
|
+
export declare function listAll<T>(fetchPage: (page: number, perPage: number) => Promise<T[]>, perPage?: number): AsyncGenerator<T, void, unknown>;
|
|
39
|
+
/**
|
|
40
|
+
* Build pagination query parameters
|
|
41
|
+
* @param options - Pagination options
|
|
42
|
+
* @returns Query parameters object
|
|
43
|
+
*/
|
|
44
|
+
export declare function buildPaginationQuery(options?: PaginationOptions): Record<string, number>;
|
|
45
|
+
//# sourceMappingURL=pagination.d.ts.map
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pagination helpers for Strava API
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Paginated iterator for auto-pagination
|
|
6
|
+
*/
|
|
7
|
+
export class PaginatedIterator {
|
|
8
|
+
constructor(fetchPage, perPage = 30) {
|
|
9
|
+
Object.defineProperty(this, "fetchPage", {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
configurable: true,
|
|
12
|
+
writable: true,
|
|
13
|
+
value: fetchPage
|
|
14
|
+
});
|
|
15
|
+
Object.defineProperty(this, "perPage", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
writable: true,
|
|
19
|
+
value: perPage
|
|
20
|
+
});
|
|
21
|
+
Object.defineProperty(this, "currentPage", {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
configurable: true,
|
|
24
|
+
writable: true,
|
|
25
|
+
value: 1
|
|
26
|
+
});
|
|
27
|
+
Object.defineProperty(this, "currentItems", {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
configurable: true,
|
|
30
|
+
writable: true,
|
|
31
|
+
value: []
|
|
32
|
+
});
|
|
33
|
+
Object.defineProperty(this, "hasMore", {
|
|
34
|
+
enumerable: true,
|
|
35
|
+
configurable: true,
|
|
36
|
+
writable: true,
|
|
37
|
+
value: true
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get next page
|
|
42
|
+
*/
|
|
43
|
+
async next() {
|
|
44
|
+
if (!this.hasMore) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
const items = await this.fetchPage(this.currentPage, this.perPage);
|
|
48
|
+
this.currentItems = items;
|
|
49
|
+
this.currentPage++;
|
|
50
|
+
if (items.length < this.perPage) {
|
|
51
|
+
this.hasMore = false;
|
|
52
|
+
}
|
|
53
|
+
return items.length > 0;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get current page items
|
|
57
|
+
*/
|
|
58
|
+
get current() {
|
|
59
|
+
return this.currentItems;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Check if there are more pages
|
|
63
|
+
*/
|
|
64
|
+
get hasMorePages() {
|
|
65
|
+
return this.hasMore;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Reset iterator
|
|
69
|
+
*/
|
|
70
|
+
reset() {
|
|
71
|
+
this.currentPage = 1;
|
|
72
|
+
this.currentItems = [];
|
|
73
|
+
this.hasMore = true;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Create async iterator for auto-pagination
|
|
78
|
+
* @param fetchPage - Function to fetch a page of data
|
|
79
|
+
* @param perPage - Number of items per page (default: 30)
|
|
80
|
+
* @returns Async generator that yields all items across pages
|
|
81
|
+
*/
|
|
82
|
+
export async function* listAll(fetchPage, perPage = 30) {
|
|
83
|
+
let page = 1;
|
|
84
|
+
let hasMore = true;
|
|
85
|
+
while (hasMore) {
|
|
86
|
+
const items = await fetchPage(page, perPage);
|
|
87
|
+
for (const item of items) {
|
|
88
|
+
yield item;
|
|
89
|
+
}
|
|
90
|
+
if (items.length < perPage) {
|
|
91
|
+
hasMore = false;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
page++;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Build pagination query parameters
|
|
100
|
+
* @param options - Pagination options
|
|
101
|
+
* @returns Query parameters object
|
|
102
|
+
*/
|
|
103
|
+
export function buildPaginationQuery(options) {
|
|
104
|
+
const query = {};
|
|
105
|
+
if (options?.page !== undefined) {
|
|
106
|
+
query.page = options.page;
|
|
107
|
+
}
|
|
108
|
+
if (options?.perPage !== undefined) {
|
|
109
|
+
query.per_page = options.perPage;
|
|
110
|
+
}
|
|
111
|
+
return query;
|
|
112
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response transformers for opinionated data transformations
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Convert snake_case keys to camelCase
|
|
6
|
+
* @param data - Data to normalize
|
|
7
|
+
* @returns Data with camelCase keys
|
|
8
|
+
*/
|
|
9
|
+
export declare function normalizeKeys<T>(data: T): T;
|
|
10
|
+
/**
|
|
11
|
+
* Transform ISO date strings to Date objects
|
|
12
|
+
* @param data - Data to transform
|
|
13
|
+
* @returns Data with Date objects instead of ISO strings
|
|
14
|
+
*/
|
|
15
|
+
export declare function transformDates<T>(data: T): T;
|
|
16
|
+
/**
|
|
17
|
+
* Flatten nested response structures
|
|
18
|
+
* @param data - Data to flatten
|
|
19
|
+
* @returns Flattened data structure
|
|
20
|
+
*/
|
|
21
|
+
export declare function flattenResponses<T>(data: T): T;
|
|
22
|
+
/**
|
|
23
|
+
* Add computed fields
|
|
24
|
+
*/
|
|
25
|
+
export declare function addComputedFields<T>(data: T): T;
|
|
26
|
+
/**
|
|
27
|
+
* Apply all transformations in order
|
|
28
|
+
*/
|
|
29
|
+
export declare function applyTransformations<T>(data: T, shouldNormalizeKeys: boolean, shouldTransformDates: boolean, shouldFlattenResponses: boolean, shouldAddComputedFields: boolean): T;
|
|
30
|
+
//# sourceMappingURL=transformers.d.ts.map
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response transformers for opinionated data transformations
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Convert snake_case keys to camelCase
|
|
6
|
+
* @param data - Data to normalize
|
|
7
|
+
* @returns Data with camelCase keys
|
|
8
|
+
*/
|
|
9
|
+
export function normalizeKeys(data) {
|
|
10
|
+
if (data === null || data === undefined) {
|
|
11
|
+
return data;
|
|
12
|
+
}
|
|
13
|
+
if (data instanceof Date) {
|
|
14
|
+
return data;
|
|
15
|
+
}
|
|
16
|
+
if (Array.isArray(data)) {
|
|
17
|
+
return data.map(normalizeKeys);
|
|
18
|
+
}
|
|
19
|
+
if (typeof data === "object") {
|
|
20
|
+
const normalized = {};
|
|
21
|
+
for (const [key, value] of Object.entries(data)) {
|
|
22
|
+
const camelKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
23
|
+
normalized[camelKey] = normalizeKeys(value);
|
|
24
|
+
}
|
|
25
|
+
return normalized;
|
|
26
|
+
}
|
|
27
|
+
return data;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Transform ISO date strings to Date objects
|
|
31
|
+
* @param data - Data to transform
|
|
32
|
+
* @returns Data with Date objects instead of ISO strings
|
|
33
|
+
*/
|
|
34
|
+
export function transformDates(data) {
|
|
35
|
+
if (data === null || data === undefined) {
|
|
36
|
+
return data;
|
|
37
|
+
}
|
|
38
|
+
if (data instanceof Date) {
|
|
39
|
+
return data;
|
|
40
|
+
}
|
|
41
|
+
if (Array.isArray(data)) {
|
|
42
|
+
return data.map(transformDates);
|
|
43
|
+
}
|
|
44
|
+
if (typeof data === "object") {
|
|
45
|
+
const transformed = {};
|
|
46
|
+
for (const [key, value] of Object.entries(data)) {
|
|
47
|
+
const lowerKey = key.toLowerCase();
|
|
48
|
+
const isDateField = /_(at|date|time)$/i.test(key) ||
|
|
49
|
+
/[dD]ate/.test(key) ||
|
|
50
|
+
/[tT]ime/.test(key) ||
|
|
51
|
+
lowerKey.includes("date") ||
|
|
52
|
+
lowerKey.includes("time");
|
|
53
|
+
if (isDateField && typeof value === "string") {
|
|
54
|
+
const date = new Date(value);
|
|
55
|
+
if (!isNaN(date.getTime())) {
|
|
56
|
+
transformed[key] = date;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
transformed[key] = transformDates(value);
|
|
61
|
+
}
|
|
62
|
+
return transformed;
|
|
63
|
+
}
|
|
64
|
+
return data;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Flatten nested response structures
|
|
68
|
+
* @param data - Data to flatten
|
|
69
|
+
* @returns Flattened data structure
|
|
70
|
+
*/
|
|
71
|
+
export function flattenResponses(data) {
|
|
72
|
+
if (data === null || data === undefined) {
|
|
73
|
+
return data;
|
|
74
|
+
}
|
|
75
|
+
if (data instanceof Date) {
|
|
76
|
+
return data;
|
|
77
|
+
}
|
|
78
|
+
if (Array.isArray(data)) {
|
|
79
|
+
return data.map(flattenResponses);
|
|
80
|
+
}
|
|
81
|
+
if (typeof data === "object") {
|
|
82
|
+
const flattened = {};
|
|
83
|
+
for (const [key, value] of Object.entries(data)) {
|
|
84
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
85
|
+
if ("id" in value || "firstname" in value || "name" in value) {
|
|
86
|
+
for (const [nestedKey, nestedValue] of Object.entries(value)) {
|
|
87
|
+
const flattenedKey = `${key}${nestedKey.charAt(0).toUpperCase() + nestedKey.slice(1)}`;
|
|
88
|
+
flattened[flattenedKey] = nestedValue;
|
|
89
|
+
}
|
|
90
|
+
flattened[key] = value;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
flattened[key] = flattenResponses(value);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
flattened[key] = flattenResponses(value);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return flattened;
|
|
101
|
+
}
|
|
102
|
+
return data;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Add computed fields
|
|
106
|
+
*/
|
|
107
|
+
export function addComputedFields(data) {
|
|
108
|
+
if (data === null || data === undefined) {
|
|
109
|
+
return data;
|
|
110
|
+
}
|
|
111
|
+
if (data instanceof Date) {
|
|
112
|
+
return data;
|
|
113
|
+
}
|
|
114
|
+
if (Array.isArray(data)) {
|
|
115
|
+
return data.map(addComputedFields);
|
|
116
|
+
}
|
|
117
|
+
if (typeof data === "object") {
|
|
118
|
+
const enhanced = { ...data };
|
|
119
|
+
const dataRecord = data;
|
|
120
|
+
if ("distance" in dataRecord && ("movingTime" in dataRecord || "elapsedTime" in dataRecord)) {
|
|
121
|
+
const distance = dataRecord.distance;
|
|
122
|
+
const movingTime = dataRecord.movingTime || dataRecord.elapsedTime;
|
|
123
|
+
if (movingTime > 0) {
|
|
124
|
+
enhanced.averageSpeed = distance / movingTime;
|
|
125
|
+
enhanced.averageSpeedKmh = (distance / 1000) / (movingTime / 3600);
|
|
126
|
+
const type = dataRecord.type;
|
|
127
|
+
const sportType = dataRecord.sportType;
|
|
128
|
+
if (type === "Run" || sportType === "Run") {
|
|
129
|
+
enhanced.pace = formatPace(movingTime / (distance / 1000));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if ("movingTime" in dataRecord || "elapsedTime" in dataRecord) {
|
|
134
|
+
const time = dataRecord.movingTime || dataRecord.elapsedTime;
|
|
135
|
+
enhanced.duration = {
|
|
136
|
+
seconds: time,
|
|
137
|
+
minutes: Math.floor(time / 60),
|
|
138
|
+
hours: Math.floor(time / 3600),
|
|
139
|
+
formatted: formatDuration(time),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
for (const [key, value] of Object.entries(enhanced)) {
|
|
143
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
144
|
+
enhanced[key] = addComputedFields(value);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return enhanced;
|
|
148
|
+
}
|
|
149
|
+
return data;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Format pace (seconds per km to min:sec/km)
|
|
153
|
+
*/
|
|
154
|
+
function formatPace(secondsPerKm) {
|
|
155
|
+
const minutes = Math.floor(secondsPerKm / 60);
|
|
156
|
+
const seconds = Math.floor(secondsPerKm % 60);
|
|
157
|
+
return `${minutes}:${seconds.toString().padStart(2, "0")}/km`;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Format duration (seconds to human-readable)
|
|
161
|
+
*/
|
|
162
|
+
function formatDuration(seconds) {
|
|
163
|
+
const hours = Math.floor(seconds / 3600);
|
|
164
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
165
|
+
const secs = Math.floor(seconds % 60);
|
|
166
|
+
if (hours > 0) {
|
|
167
|
+
return `${hours}:${minutes.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
|
|
168
|
+
}
|
|
169
|
+
return `${minutes}:${secs.toString().padStart(2, "0")}`;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Apply all transformations in order
|
|
173
|
+
*/
|
|
174
|
+
export function applyTransformations(data, shouldNormalizeKeys, shouldTransformDates, shouldFlattenResponses, shouldAddComputedFields) {
|
|
175
|
+
let result = data;
|
|
176
|
+
if (shouldNormalizeKeys) {
|
|
177
|
+
result = normalizeKeys(result);
|
|
178
|
+
}
|
|
179
|
+
if (shouldTransformDates) {
|
|
180
|
+
result = transformDates(result);
|
|
181
|
+
}
|
|
182
|
+
if (shouldFlattenResponses) {
|
|
183
|
+
result = flattenResponses(result);
|
|
184
|
+
}
|
|
185
|
+
if (shouldAddComputedFields) {
|
|
186
|
+
result = addComputedFields(result);
|
|
187
|
+
}
|
|
188
|
+
return result;
|
|
189
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input validation helpers
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Validate that a value is a positive integer
|
|
6
|
+
* @param value - Value to validate
|
|
7
|
+
* @param name - Name of the parameter for error messages
|
|
8
|
+
* @returns Validated positive integer
|
|
9
|
+
* @throws Error if value is not a positive integer
|
|
10
|
+
*/
|
|
11
|
+
export declare function validatePositiveInteger(value: unknown, name: string): number;
|
|
12
|
+
/**
|
|
13
|
+
* Validate that a value is a non-negative integer
|
|
14
|
+
* @param value - Value to validate
|
|
15
|
+
* @param name - Name of the parameter for error messages
|
|
16
|
+
* @returns Validated non-negative integer
|
|
17
|
+
* @throws Error if value is not a non-negative integer
|
|
18
|
+
*/
|
|
19
|
+
export declare function validateNonNegativeInteger(value: unknown, name: string): number;
|
|
20
|
+
/**
|
|
21
|
+
* Validate that a value is within a range
|
|
22
|
+
* @param value - Value to validate
|
|
23
|
+
* @param min - Minimum allowed value
|
|
24
|
+
* @param max - Maximum allowed value
|
|
25
|
+
* @param name - Name of the parameter for error messages
|
|
26
|
+
* @returns Validated value
|
|
27
|
+
* @throws Error if value is outside the range
|
|
28
|
+
*/
|
|
29
|
+
export declare function validateRange(value: number, min: number, max: number, name: string): number;
|
|
30
|
+
/**
|
|
31
|
+
* Validate that a value is a valid date
|
|
32
|
+
* @param value - Value to validate
|
|
33
|
+
* @param name - Name of the parameter for error messages
|
|
34
|
+
* @returns Validated Date object
|
|
35
|
+
* @throws Error if value is not a valid Date
|
|
36
|
+
*/
|
|
37
|
+
export declare function validateDate(value: unknown, name: string): Date;
|
|
38
|
+
/**
|
|
39
|
+
* Validate that a value is a non-empty string
|
|
40
|
+
* @param value - Value to validate
|
|
41
|
+
* @param name - Name of the parameter for error messages
|
|
42
|
+
* @returns Validated non-empty string
|
|
43
|
+
* @throws Error if value is not a non-empty string
|
|
44
|
+
*/
|
|
45
|
+
export declare function validateNonEmptyString(value: unknown, name: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Validate pagination options
|
|
48
|
+
* @param page - Page number (optional)
|
|
49
|
+
* @param perPage - Items per page (optional, must be 1-200)
|
|
50
|
+
* @throws Error if pagination options are invalid
|
|
51
|
+
*/
|
|
52
|
+
export declare function validatePagination(page?: number, perPage?: number): void;
|
|
53
|
+
//# sourceMappingURL=validators.d.ts.map
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input validation helpers
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Validate that a value is a positive integer
|
|
6
|
+
* @param value - Value to validate
|
|
7
|
+
* @param name - Name of the parameter for error messages
|
|
8
|
+
* @returns Validated positive integer
|
|
9
|
+
* @throws Error if value is not a positive integer
|
|
10
|
+
*/
|
|
11
|
+
export function validatePositiveInteger(value, name) {
|
|
12
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
13
|
+
throw new Error(`${name} must be a positive integer, got: ${value}`);
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Validate that a value is a non-negative integer
|
|
19
|
+
* @param value - Value to validate
|
|
20
|
+
* @param name - Name of the parameter for error messages
|
|
21
|
+
* @returns Validated non-negative integer
|
|
22
|
+
* @throws Error if value is not a non-negative integer
|
|
23
|
+
*/
|
|
24
|
+
export function validateNonNegativeInteger(value, name) {
|
|
25
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
26
|
+
throw new Error(`${name} must be a non-negative integer, got: ${value}`);
|
|
27
|
+
}
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Validate that a value is within a range
|
|
32
|
+
* @param value - Value to validate
|
|
33
|
+
* @param min - Minimum allowed value
|
|
34
|
+
* @param max - Maximum allowed value
|
|
35
|
+
* @param name - Name of the parameter for error messages
|
|
36
|
+
* @returns Validated value
|
|
37
|
+
* @throws Error if value is outside the range
|
|
38
|
+
*/
|
|
39
|
+
export function validateRange(value, min, max, name) {
|
|
40
|
+
if (value < min || value > max) {
|
|
41
|
+
throw new Error(`${name} must be between ${min} and ${max}, got: ${value}`);
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Validate that a value is a valid date
|
|
47
|
+
* @param value - Value to validate
|
|
48
|
+
* @param name - Name of the parameter for error messages
|
|
49
|
+
* @returns Validated Date object
|
|
50
|
+
* @throws Error if value is not a valid Date
|
|
51
|
+
*/
|
|
52
|
+
export function validateDate(value, name) {
|
|
53
|
+
if (!(value instanceof Date) || isNaN(value.getTime())) {
|
|
54
|
+
throw new Error(`${name} must be a valid Date object`);
|
|
55
|
+
}
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Validate that a value is a non-empty string
|
|
60
|
+
* @param value - Value to validate
|
|
61
|
+
* @param name - Name of the parameter for error messages
|
|
62
|
+
* @returns Validated non-empty string
|
|
63
|
+
* @throws Error if value is not a non-empty string
|
|
64
|
+
*/
|
|
65
|
+
export function validateNonEmptyString(value, name) {
|
|
66
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
67
|
+
throw new Error(`${name} must be a non-empty string`);
|
|
68
|
+
}
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Validate pagination options
|
|
73
|
+
* @param page - Page number (optional)
|
|
74
|
+
* @param perPage - Items per page (optional, must be 1-200)
|
|
75
|
+
* @throws Error if pagination options are invalid
|
|
76
|
+
*/
|
|
77
|
+
export function validatePagination(page, perPage) {
|
|
78
|
+
if (page !== undefined) {
|
|
79
|
+
validatePositiveInteger(page, "page");
|
|
80
|
+
}
|
|
81
|
+
if (perPage !== undefined) {
|
|
82
|
+
validateRange(perPage, 1, 200, "perPage");
|
|
83
|
+
}
|
|
84
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pinta365/strava",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A library for interacting with the Strava API.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"strava",
|
|
7
|
+
"api",
|
|
8
|
+
"fitness",
|
|
9
|
+
"sports",
|
|
10
|
+
"running",
|
|
11
|
+
"cycling",
|
|
12
|
+
"cross-runtime",
|
|
13
|
+
"deno",
|
|
14
|
+
"node",
|
|
15
|
+
"bun",
|
|
16
|
+
"typescript"
|
|
17
|
+
],
|
|
18
|
+
"homepage": "https://github.com/Pinta365/strava",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/Pinta365/strava.git"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/Pinta365/strava/issues"
|
|
26
|
+
},
|
|
27
|
+
"main": "./script/mod.js",
|
|
28
|
+
"module": "./esm/mod.js",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"import": "./esm/mod.js",
|
|
32
|
+
"require": "./script/mod.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"scripts": {},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
},
|
|
39
|
+
"_generatedBy": "dnt@dev"
|
|
40
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dntGlobalThis = void 0;
|
|
4
|
+
const dntGlobals = {};
|
|
5
|
+
exports.dntGlobalThis = createMergeProxy(globalThis, dntGlobals);
|
|
6
|
+
function createMergeProxy(baseObj, extObj) {
|
|
7
|
+
return new Proxy(baseObj, {
|
|
8
|
+
get(_target, prop, _receiver) {
|
|
9
|
+
if (prop in extObj) {
|
|
10
|
+
return extObj[prop];
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
return baseObj[prop];
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
set(_target, prop, value) {
|
|
17
|
+
if (prop in extObj) {
|
|
18
|
+
delete extObj[prop];
|
|
19
|
+
}
|
|
20
|
+
baseObj[prop] = value;
|
|
21
|
+
return true;
|
|
22
|
+
},
|
|
23
|
+
deleteProperty(_target, prop) {
|
|
24
|
+
let success = false;
|
|
25
|
+
if (prop in extObj) {
|
|
26
|
+
delete extObj[prop];
|
|
27
|
+
success = true;
|
|
28
|
+
}
|
|
29
|
+
if (prop in baseObj) {
|
|
30
|
+
delete baseObj[prop];
|
|
31
|
+
success = true;
|
|
32
|
+
}
|
|
33
|
+
return success;
|
|
34
|
+
},
|
|
35
|
+
ownKeys(_target) {
|
|
36
|
+
const baseKeys = Reflect.ownKeys(baseObj);
|
|
37
|
+
const extKeys = Reflect.ownKeys(extObj);
|
|
38
|
+
const extKeysSet = new Set(extKeys);
|
|
39
|
+
return [...baseKeys.filter((k) => !extKeysSet.has(k)), ...extKeys];
|
|
40
|
+
},
|
|
41
|
+
defineProperty(_target, prop, desc) {
|
|
42
|
+
if (prop in extObj) {
|
|
43
|
+
delete extObj[prop];
|
|
44
|
+
}
|
|
45
|
+
Reflect.defineProperty(baseObj, prop, desc);
|
|
46
|
+
return true;
|
|
47
|
+
},
|
|
48
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
49
|
+
if (prop in extObj) {
|
|
50
|
+
return Reflect.getOwnPropertyDescriptor(extObj, prop);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
return Reflect.getOwnPropertyDescriptor(baseObj, prop);
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
has(_target, prop) {
|
|
57
|
+
return prop in extObj || prop in baseObj;
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|