@getlatedev/node 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +190 -0
- package/README.md +232 -0
- package/dist/index.d.mts +4115 -0
- package/dist/index.d.ts +4115 -0
- package/dist/index.js +1135 -0
- package/dist/index.mjs +1106 -0
- package/package.json +79 -0
- package/src/client.ts +425 -0
- package/src/errors.ts +144 -0
- package/src/generated/index.ts +3 -0
- package/src/generated/sdk.gen.ts +1430 -0
- package/src/generated/types.gen.ts +4162 -0
- package/src/index.ts +34 -0
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@getlatedev/node",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The official Node.js library for the Late API",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./_shims/*": {
|
|
16
|
+
"types": "./dist/_shims/*.d.ts",
|
|
17
|
+
"import": "./dist/_shims/*.mjs",
|
|
18
|
+
"require": "./dist/_shims/*.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"src",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"generate": "openapi-ts -i openapi.yaml -o src/generated -c @hey-api/client-fetch",
|
|
29
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
30
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
31
|
+
"lint": "eslint src --ext .ts",
|
|
32
|
+
"typecheck": "tsc --noEmit",
|
|
33
|
+
"test": "vitest run",
|
|
34
|
+
"test:watch": "vitest",
|
|
35
|
+
"prepublishOnly": "npm run build",
|
|
36
|
+
"fetch-spec": "curl -o openapi.yaml https://getlate.dev/openapi.yaml"
|
|
37
|
+
},
|
|
38
|
+
"keywords": [
|
|
39
|
+
"late",
|
|
40
|
+
"social-media",
|
|
41
|
+
"scheduling",
|
|
42
|
+
"api",
|
|
43
|
+
"sdk",
|
|
44
|
+
"instagram",
|
|
45
|
+
"tiktok",
|
|
46
|
+
"youtube",
|
|
47
|
+
"linkedin",
|
|
48
|
+
"twitter",
|
|
49
|
+
"x",
|
|
50
|
+
"facebook",
|
|
51
|
+
"pinterest",
|
|
52
|
+
"threads",
|
|
53
|
+
"bluesky",
|
|
54
|
+
"snapchat",
|
|
55
|
+
"telegram"
|
|
56
|
+
],
|
|
57
|
+
"author": "Late <support@getlate.dev>",
|
|
58
|
+
"license": "Apache-2.0",
|
|
59
|
+
"repository": {
|
|
60
|
+
"type": "git",
|
|
61
|
+
"url": "git+https://github.com/getlatedev/late-node.git"
|
|
62
|
+
},
|
|
63
|
+
"homepage": "https://getlate.dev/docs/api",
|
|
64
|
+
"bugs": {
|
|
65
|
+
"url": "https://github.com/getlatedev/late-node/issues"
|
|
66
|
+
},
|
|
67
|
+
"engines": {
|
|
68
|
+
"node": ">=18"
|
|
69
|
+
},
|
|
70
|
+
"devDependencies": {
|
|
71
|
+
"@hey-api/client-fetch": "^0.6.0",
|
|
72
|
+
"@hey-api/openapi-ts": "^0.61.0",
|
|
73
|
+
"@types/node": "^20.11.0",
|
|
74
|
+
"eslint": "^8.56.0",
|
|
75
|
+
"tsup": "^8.0.1",
|
|
76
|
+
"typescript": "^5.3.3",
|
|
77
|
+
"vitest": "^1.2.0"
|
|
78
|
+
}
|
|
79
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
import {
|
|
2
|
+
client,
|
|
3
|
+
// Posts
|
|
4
|
+
getV1Posts,
|
|
5
|
+
postV1Posts,
|
|
6
|
+
getV1PostsByPostId,
|
|
7
|
+
putV1PostsByPostId,
|
|
8
|
+
deleteV1PostsByPostId,
|
|
9
|
+
postV1PostsByPostIdRetry,
|
|
10
|
+
postV1PostsBulkUpload,
|
|
11
|
+
getV1PostsByPostIdLogs,
|
|
12
|
+
// Accounts
|
|
13
|
+
getV1Accounts,
|
|
14
|
+
putV1AccountsByAccountId,
|
|
15
|
+
deleteV1AccountsByAccountId,
|
|
16
|
+
getV1AccountsFollowerStats,
|
|
17
|
+
getV1AccountsHealth,
|
|
18
|
+
getV1AccountsByAccountIdHealth,
|
|
19
|
+
// Profiles
|
|
20
|
+
getV1Profiles,
|
|
21
|
+
postV1Profiles,
|
|
22
|
+
getV1ProfilesByProfileId,
|
|
23
|
+
putV1ProfilesByProfileId,
|
|
24
|
+
deleteV1ProfilesByProfileId,
|
|
25
|
+
// Analytics
|
|
26
|
+
getV1Analytics,
|
|
27
|
+
getV1AnalyticsYoutubeDailyViews,
|
|
28
|
+
getV1AccountsByAccountIdLinkedinAggregateAnalytics,
|
|
29
|
+
getV1AccountsByAccountIdLinkedinPostAnalytics,
|
|
30
|
+
// Account Groups
|
|
31
|
+
getV1AccountGroups,
|
|
32
|
+
postV1AccountGroups,
|
|
33
|
+
putV1AccountGroupsByGroupId,
|
|
34
|
+
deleteV1AccountGroupsByGroupId,
|
|
35
|
+
// Queue
|
|
36
|
+
getV1QueueSlots,
|
|
37
|
+
postV1QueueSlots,
|
|
38
|
+
putV1QueueSlots,
|
|
39
|
+
deleteV1QueueSlots,
|
|
40
|
+
getV1QueuePreview,
|
|
41
|
+
getV1QueueNextSlot,
|
|
42
|
+
// Webhooks
|
|
43
|
+
getV1WebhooksSettings,
|
|
44
|
+
postV1WebhooksSettings,
|
|
45
|
+
putV1WebhooksSettings,
|
|
46
|
+
deleteV1WebhooksSettings,
|
|
47
|
+
postV1WebhooksTest,
|
|
48
|
+
getV1WebhooksLogs,
|
|
49
|
+
// API Keys
|
|
50
|
+
getV1ApiKeys,
|
|
51
|
+
postV1ApiKeys,
|
|
52
|
+
deleteV1ApiKeysByKeyId,
|
|
53
|
+
// Media
|
|
54
|
+
postV1MediaPresign,
|
|
55
|
+
// Tools
|
|
56
|
+
getV1ToolsYoutubeDownload,
|
|
57
|
+
getV1ToolsYoutubeTranscript,
|
|
58
|
+
getV1ToolsInstagramDownload,
|
|
59
|
+
postV1ToolsInstagramHashtagChecker,
|
|
60
|
+
getV1ToolsTiktokDownload,
|
|
61
|
+
getV1ToolsTwitterDownload,
|
|
62
|
+
getV1ToolsFacebookDownload,
|
|
63
|
+
getV1ToolsLinkedinDownload,
|
|
64
|
+
getV1ToolsBlueskyDownload,
|
|
65
|
+
// Users
|
|
66
|
+
getV1Users,
|
|
67
|
+
getV1UsersByUserId,
|
|
68
|
+
// Usage
|
|
69
|
+
getV1UsageStats,
|
|
70
|
+
// Logs
|
|
71
|
+
getV1Logs,
|
|
72
|
+
getV1LogsByLogId,
|
|
73
|
+
// Connect
|
|
74
|
+
getV1ConnectByPlatform,
|
|
75
|
+
postV1ConnectByPlatform,
|
|
76
|
+
getV1ConnectFacebookSelectPage,
|
|
77
|
+
postV1ConnectFacebookSelectPage,
|
|
78
|
+
getV1ConnectGooglebusinessLocations,
|
|
79
|
+
postV1ConnectGooglebusinessSelectLocation,
|
|
80
|
+
getV1ConnectLinkedinOrganizations,
|
|
81
|
+
postV1ConnectLinkedinSelectOrganization,
|
|
82
|
+
getV1ConnectPinterestSelectBoard,
|
|
83
|
+
postV1ConnectPinterestSelectBoard,
|
|
84
|
+
getV1ConnectSnapchatSelectProfile,
|
|
85
|
+
postV1ConnectSnapchatSelectProfile,
|
|
86
|
+
postV1ConnectBlueskyCredentials,
|
|
87
|
+
getV1ConnectTelegram,
|
|
88
|
+
postV1ConnectTelegram,
|
|
89
|
+
patchV1ConnectTelegram,
|
|
90
|
+
// Reddit
|
|
91
|
+
getV1RedditSearch,
|
|
92
|
+
getV1RedditFeed,
|
|
93
|
+
// Invites
|
|
94
|
+
postV1InviteTokens,
|
|
95
|
+
getV1PlatformInvites,
|
|
96
|
+
postV1PlatformInvites,
|
|
97
|
+
deleteV1PlatformInvites,
|
|
98
|
+
// Account-specific endpoints
|
|
99
|
+
putV1AccountsByAccountIdFacebookPage,
|
|
100
|
+
getV1AccountsByAccountIdLinkedinOrganizations,
|
|
101
|
+
putV1AccountsByAccountIdLinkedinOrganization,
|
|
102
|
+
getV1AccountsByAccountIdLinkedinMentions,
|
|
103
|
+
getV1AccountsByAccountIdPinterestBoards,
|
|
104
|
+
putV1AccountsByAccountIdPinterestBoards,
|
|
105
|
+
getV1AccountsByAccountIdRedditSubreddits,
|
|
106
|
+
putV1AccountsByAccountIdRedditSubreddits,
|
|
107
|
+
getV1AccountsByAccountIdGmbReviews,
|
|
108
|
+
} from './generated/sdk.gen';
|
|
109
|
+
|
|
110
|
+
import { LateApiError, parseApiError } from './errors';
|
|
111
|
+
|
|
112
|
+
export interface ClientOptions {
|
|
113
|
+
/**
|
|
114
|
+
* API key for authentication. Defaults to process.env['LATE_API_KEY'].
|
|
115
|
+
*/
|
|
116
|
+
apiKey?: string | undefined;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Override the default base URL for the API.
|
|
120
|
+
* @default "https://getlate.dev/api"
|
|
121
|
+
*/
|
|
122
|
+
baseURL?: string | null | undefined;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* The maximum amount of time (in milliseconds) that the client should wait for a response.
|
|
126
|
+
* @default 60000
|
|
127
|
+
*/
|
|
128
|
+
timeout?: number;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Default headers to include with every request.
|
|
132
|
+
*/
|
|
133
|
+
defaultHeaders?: Record<string, string>;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* API Client for the Late API.
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```typescript
|
|
141
|
+
* import Late from '@getlatedev/node';
|
|
142
|
+
*
|
|
143
|
+
* const late = new Late({
|
|
144
|
+
* apiKey: process.env['LATE_API_KEY'], // This is the default and can be omitted
|
|
145
|
+
* });
|
|
146
|
+
*
|
|
147
|
+
* async function main() {
|
|
148
|
+
* const post = await late.posts.create({
|
|
149
|
+
* body: {
|
|
150
|
+
* content: 'Hello from the Late SDK!',
|
|
151
|
+
* platforms: [{ platform: 'twitter', accountId: 'acc_123' }],
|
|
152
|
+
* publishNow: true,
|
|
153
|
+
* },
|
|
154
|
+
* });
|
|
155
|
+
* console.log(post.data);
|
|
156
|
+
* }
|
|
157
|
+
*
|
|
158
|
+
* main();
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
export class Late {
|
|
162
|
+
private _options: ClientOptions;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* API key used for authentication.
|
|
166
|
+
*/
|
|
167
|
+
apiKey: string;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Base URL for API requests.
|
|
171
|
+
*/
|
|
172
|
+
baseURL: string;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Posts API - Create, schedule, and manage social media posts
|
|
176
|
+
*/
|
|
177
|
+
posts = {
|
|
178
|
+
list: getV1Posts,
|
|
179
|
+
create: postV1Posts,
|
|
180
|
+
get: getV1PostsByPostId,
|
|
181
|
+
update: putV1PostsByPostId,
|
|
182
|
+
delete: deleteV1PostsByPostId,
|
|
183
|
+
retry: postV1PostsByPostIdRetry,
|
|
184
|
+
bulkUpload: postV1PostsBulkUpload,
|
|
185
|
+
getLogs: getV1PostsByPostIdLogs,
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Accounts API - Manage connected social media accounts
|
|
190
|
+
*/
|
|
191
|
+
accounts = {
|
|
192
|
+
list: getV1Accounts,
|
|
193
|
+
update: putV1AccountsByAccountId,
|
|
194
|
+
delete: deleteV1AccountsByAccountId,
|
|
195
|
+
getFollowerStats: getV1AccountsFollowerStats,
|
|
196
|
+
getAllHealth: getV1AccountsHealth,
|
|
197
|
+
getHealth: getV1AccountsByAccountIdHealth,
|
|
198
|
+
updateFacebookPage: putV1AccountsByAccountIdFacebookPage,
|
|
199
|
+
getLinkedInOrganizations: getV1AccountsByAccountIdLinkedinOrganizations,
|
|
200
|
+
updateLinkedInOrganization: putV1AccountsByAccountIdLinkedinOrganization,
|
|
201
|
+
getLinkedInMentions: getV1AccountsByAccountIdLinkedinMentions,
|
|
202
|
+
getPinterestBoards: getV1AccountsByAccountIdPinterestBoards,
|
|
203
|
+
updatePinterestBoards: putV1AccountsByAccountIdPinterestBoards,
|
|
204
|
+
getRedditSubreddits: getV1AccountsByAccountIdRedditSubreddits,
|
|
205
|
+
updateRedditSubreddits: putV1AccountsByAccountIdRedditSubreddits,
|
|
206
|
+
getGoogleBusinessReviews: getV1AccountsByAccountIdGmbReviews,
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Profiles API - Manage workspace profiles
|
|
211
|
+
*/
|
|
212
|
+
profiles = {
|
|
213
|
+
list: getV1Profiles,
|
|
214
|
+
create: postV1Profiles,
|
|
215
|
+
get: getV1ProfilesByProfileId,
|
|
216
|
+
update: putV1ProfilesByProfileId,
|
|
217
|
+
delete: deleteV1ProfilesByProfileId,
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Analytics API - Get performance metrics
|
|
222
|
+
*/
|
|
223
|
+
analytics = {
|
|
224
|
+
get: getV1Analytics,
|
|
225
|
+
getYouTubeDailyViews: getV1AnalyticsYoutubeDailyViews,
|
|
226
|
+
getLinkedInAggregate: getV1AccountsByAccountIdLinkedinAggregateAnalytics,
|
|
227
|
+
getLinkedInPostAnalytics: getV1AccountsByAccountIdLinkedinPostAnalytics,
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Account Groups API - Organize accounts into groups
|
|
232
|
+
*/
|
|
233
|
+
accountGroups = {
|
|
234
|
+
list: getV1AccountGroups,
|
|
235
|
+
create: postV1AccountGroups,
|
|
236
|
+
update: putV1AccountGroupsByGroupId,
|
|
237
|
+
delete: deleteV1AccountGroupsByGroupId,
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Queue API - Manage posting queue
|
|
242
|
+
*/
|
|
243
|
+
queue = {
|
|
244
|
+
listSlots: getV1QueueSlots,
|
|
245
|
+
createSlot: postV1QueueSlots,
|
|
246
|
+
updateSlot: putV1QueueSlots,
|
|
247
|
+
deleteSlot: deleteV1QueueSlots,
|
|
248
|
+
preview: getV1QueuePreview,
|
|
249
|
+
getNextSlot: getV1QueueNextSlot,
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Webhooks API - Configure event webhooks
|
|
254
|
+
*/
|
|
255
|
+
webhooks = {
|
|
256
|
+
getSettings: getV1WebhooksSettings,
|
|
257
|
+
createSettings: postV1WebhooksSettings,
|
|
258
|
+
updateSettings: putV1WebhooksSettings,
|
|
259
|
+
deleteSettings: deleteV1WebhooksSettings,
|
|
260
|
+
test: postV1WebhooksTest,
|
|
261
|
+
getLogs: getV1WebhooksLogs,
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* API Keys API - Manage API keys
|
|
266
|
+
*/
|
|
267
|
+
apiKeys = {
|
|
268
|
+
list: getV1ApiKeys,
|
|
269
|
+
create: postV1ApiKeys,
|
|
270
|
+
delete: deleteV1ApiKeysByKeyId,
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Media API - Upload and manage media files
|
|
275
|
+
*/
|
|
276
|
+
media = {
|
|
277
|
+
getPresignedUrl: postV1MediaPresign,
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Tools API - Media download and utilities
|
|
282
|
+
*/
|
|
283
|
+
tools = {
|
|
284
|
+
downloadYouTube: getV1ToolsYoutubeDownload,
|
|
285
|
+
getYouTubeTranscript: getV1ToolsYoutubeTranscript,
|
|
286
|
+
downloadInstagram: getV1ToolsInstagramDownload,
|
|
287
|
+
checkInstagramHashtags: postV1ToolsInstagramHashtagChecker,
|
|
288
|
+
downloadTikTok: getV1ToolsTiktokDownload,
|
|
289
|
+
downloadTwitter: getV1ToolsTwitterDownload,
|
|
290
|
+
downloadFacebook: getV1ToolsFacebookDownload,
|
|
291
|
+
downloadLinkedIn: getV1ToolsLinkedinDownload,
|
|
292
|
+
downloadBluesky: getV1ToolsBlueskyDownload,
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Users API - User management
|
|
297
|
+
*/
|
|
298
|
+
users = {
|
|
299
|
+
list: getV1Users,
|
|
300
|
+
get: getV1UsersByUserId,
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Usage API - Get usage statistics
|
|
305
|
+
*/
|
|
306
|
+
usage = {
|
|
307
|
+
getStats: getV1UsageStats,
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Logs API - Publishing logs
|
|
312
|
+
*/
|
|
313
|
+
logs = {
|
|
314
|
+
list: getV1Logs,
|
|
315
|
+
get: getV1LogsByLogId,
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Connect API - OAuth connection flows
|
|
320
|
+
*/
|
|
321
|
+
connect = {
|
|
322
|
+
getUrl: getV1ConnectByPlatform,
|
|
323
|
+
handleCallback: postV1ConnectByPlatform,
|
|
324
|
+
facebook: {
|
|
325
|
+
listPages: getV1ConnectFacebookSelectPage,
|
|
326
|
+
selectPage: postV1ConnectFacebookSelectPage,
|
|
327
|
+
},
|
|
328
|
+
googleBusiness: {
|
|
329
|
+
listLocations: getV1ConnectGooglebusinessLocations,
|
|
330
|
+
selectLocation: postV1ConnectGooglebusinessSelectLocation,
|
|
331
|
+
},
|
|
332
|
+
linkedIn: {
|
|
333
|
+
listOrganizations: getV1ConnectLinkedinOrganizations,
|
|
334
|
+
selectOrganization: postV1ConnectLinkedinSelectOrganization,
|
|
335
|
+
},
|
|
336
|
+
pinterest: {
|
|
337
|
+
listBoards: getV1ConnectPinterestSelectBoard,
|
|
338
|
+
selectBoard: postV1ConnectPinterestSelectBoard,
|
|
339
|
+
},
|
|
340
|
+
snapchat: {
|
|
341
|
+
listProfiles: getV1ConnectSnapchatSelectProfile,
|
|
342
|
+
selectProfile: postV1ConnectSnapchatSelectProfile,
|
|
343
|
+
},
|
|
344
|
+
bluesky: {
|
|
345
|
+
connectCredentials: postV1ConnectBlueskyCredentials,
|
|
346
|
+
},
|
|
347
|
+
telegram: {
|
|
348
|
+
getStatus: getV1ConnectTelegram,
|
|
349
|
+
initiate: postV1ConnectTelegram,
|
|
350
|
+
complete: patchV1ConnectTelegram,
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Reddit API - Search and feed
|
|
356
|
+
*/
|
|
357
|
+
reddit = {
|
|
358
|
+
search: getV1RedditSearch,
|
|
359
|
+
getFeed: getV1RedditFeed,
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Invites API - Team invitations
|
|
364
|
+
*/
|
|
365
|
+
invites = {
|
|
366
|
+
createToken: postV1InviteTokens,
|
|
367
|
+
list: getV1PlatformInvites,
|
|
368
|
+
create: postV1PlatformInvites,
|
|
369
|
+
delete: deleteV1PlatformInvites,
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Create a new Late API client.
|
|
374
|
+
*
|
|
375
|
+
* @param options - Configuration options for the client
|
|
376
|
+
*/
|
|
377
|
+
constructor(options: ClientOptions = {}) {
|
|
378
|
+
const apiKey = options.apiKey ?? process.env['LATE_API_KEY'];
|
|
379
|
+
|
|
380
|
+
if (!apiKey) {
|
|
381
|
+
throw new LateApiError(
|
|
382
|
+
"The LATE_API_KEY environment variable is missing or empty; either provide it, or instantiate the Late client with an apiKey option, like new Late({ apiKey: 'sk_...' }).",
|
|
383
|
+
401,
|
|
384
|
+
'missing_api_key'
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
this.apiKey = apiKey;
|
|
389
|
+
this.baseURL = options.baseURL ?? 'https://getlate.dev/api';
|
|
390
|
+
this._options = options;
|
|
391
|
+
|
|
392
|
+
// Configure the generated client
|
|
393
|
+
client.setConfig({
|
|
394
|
+
baseUrl: this.baseURL,
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
// Add auth interceptor
|
|
398
|
+
client.interceptors.request.use((request) => {
|
|
399
|
+
request.headers.set('Authorization', `Bearer ${this.apiKey}`);
|
|
400
|
+
if (options.defaultHeaders) {
|
|
401
|
+
for (const [key, value] of Object.entries(options.defaultHeaders)) {
|
|
402
|
+
request.headers.set(key, value);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return request;
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
// Add error handling interceptor
|
|
409
|
+
client.interceptors.response.use(async (response) => {
|
|
410
|
+
if (!response.ok) {
|
|
411
|
+
let body: Record<string, unknown> | undefined;
|
|
412
|
+
try {
|
|
413
|
+
body = (await response.clone().json()) as Record<string, unknown>;
|
|
414
|
+
} catch {
|
|
415
|
+
// Ignore JSON parse errors
|
|
416
|
+
}
|
|
417
|
+
throw parseApiError(response, body);
|
|
418
|
+
}
|
|
419
|
+
return response;
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Default export for convenient usage
|
|
425
|
+
export default Late;
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base error class for Late API errors
|
|
3
|
+
*/
|
|
4
|
+
export class LateApiError extends Error {
|
|
5
|
+
public readonly statusCode: number;
|
|
6
|
+
public readonly code?: string;
|
|
7
|
+
public readonly details?: Record<string, unknown>;
|
|
8
|
+
|
|
9
|
+
constructor(
|
|
10
|
+
message: string,
|
|
11
|
+
statusCode: number,
|
|
12
|
+
code?: string,
|
|
13
|
+
details?: Record<string, unknown>
|
|
14
|
+
) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = 'LateApiError';
|
|
17
|
+
this.statusCode = statusCode;
|
|
18
|
+
this.code = code;
|
|
19
|
+
this.details = details;
|
|
20
|
+
|
|
21
|
+
// Maintains proper stack trace for where error was thrown
|
|
22
|
+
if (Error.captureStackTrace) {
|
|
23
|
+
Error.captureStackTrace(this, LateApiError);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if this is a rate limit error
|
|
29
|
+
*/
|
|
30
|
+
isRateLimited(): boolean {
|
|
31
|
+
return this.statusCode === 429;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Check if this is an authentication error
|
|
36
|
+
*/
|
|
37
|
+
isAuthError(): boolean {
|
|
38
|
+
return this.statusCode === 401;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Check if this is a permission/access error
|
|
43
|
+
*/
|
|
44
|
+
isForbidden(): boolean {
|
|
45
|
+
return this.statusCode === 403;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Check if this is a not found error
|
|
50
|
+
*/
|
|
51
|
+
isNotFound(): boolean {
|
|
52
|
+
return this.statusCode === 404;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Check if this is a validation error
|
|
57
|
+
*/
|
|
58
|
+
isValidationError(): boolean {
|
|
59
|
+
return this.statusCode === 400;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Check if this is a payment required error
|
|
64
|
+
*/
|
|
65
|
+
isPaymentRequired(): boolean {
|
|
66
|
+
return this.statusCode === 402;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Rate limit error with additional rate limit info
|
|
72
|
+
*/
|
|
73
|
+
export class RateLimitError extends LateApiError {
|
|
74
|
+
public readonly limit?: number;
|
|
75
|
+
public readonly remaining?: number;
|
|
76
|
+
public readonly resetAt?: Date;
|
|
77
|
+
|
|
78
|
+
constructor(
|
|
79
|
+
message: string,
|
|
80
|
+
limit?: number,
|
|
81
|
+
remaining?: number,
|
|
82
|
+
resetAt?: Date
|
|
83
|
+
) {
|
|
84
|
+
super(message, 429, 'rate_limit_exceeded');
|
|
85
|
+
this.name = 'RateLimitError';
|
|
86
|
+
this.limit = limit;
|
|
87
|
+
this.remaining = remaining;
|
|
88
|
+
this.resetAt = resetAt;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Get seconds until rate limit resets
|
|
93
|
+
*/
|
|
94
|
+
getSecondsUntilReset(): number | undefined {
|
|
95
|
+
if (!this.resetAt) return undefined;
|
|
96
|
+
return Math.max(0, Math.ceil((this.resetAt.getTime() - Date.now()) / 1000));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Validation error with field-specific details
|
|
102
|
+
*/
|
|
103
|
+
export class ValidationError extends LateApiError {
|
|
104
|
+
public readonly fields?: Record<string, string[]>;
|
|
105
|
+
|
|
106
|
+
constructor(message: string, fields?: Record<string, string[]>) {
|
|
107
|
+
super(message, 400, 'validation_error', { fields });
|
|
108
|
+
this.name = 'ValidationError';
|
|
109
|
+
this.fields = fields;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Parse an error response from the API
|
|
115
|
+
*/
|
|
116
|
+
export function parseApiError(
|
|
117
|
+
response: Response,
|
|
118
|
+
body?: { error?: string; message?: string; code?: string; details?: Record<string, unknown> }
|
|
119
|
+
): LateApiError {
|
|
120
|
+
const message = body?.error || body?.message || response.statusText || 'Unknown error';
|
|
121
|
+
const code = body?.code;
|
|
122
|
+
const details = body?.details;
|
|
123
|
+
|
|
124
|
+
// Handle rate limit errors
|
|
125
|
+
if (response.status === 429) {
|
|
126
|
+
const limit = response.headers.get('X-RateLimit-Limit');
|
|
127
|
+
const remaining = response.headers.get('X-RateLimit-Remaining');
|
|
128
|
+
const reset = response.headers.get('X-RateLimit-Reset');
|
|
129
|
+
|
|
130
|
+
return new RateLimitError(
|
|
131
|
+
message,
|
|
132
|
+
limit ? parseInt(limit, 10) : undefined,
|
|
133
|
+
remaining ? parseInt(remaining, 10) : undefined,
|
|
134
|
+
reset ? new Date(parseInt(reset, 10) * 1000) : undefined
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Handle validation errors
|
|
139
|
+
if (response.status === 400 && details?.fields) {
|
|
140
|
+
return new ValidationError(message, details.fields as Record<string, string[]>);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return new LateApiError(message, response.status, code, details);
|
|
144
|
+
}
|