@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/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
+ }
@@ -0,0 +1,3 @@
1
+ // This file is auto-generated by @hey-api/openapi-ts
2
+ export * from './sdk.gen';
3
+ export * from './types.gen';