@addn/sdk 1.0.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.
Files changed (156) hide show
  1. package/dist/auth/storage.d.ts +25 -0
  2. package/dist/auth/storage.d.ts.map +1 -0
  3. package/dist/auth/storage.js +72 -0
  4. package/dist/auth/storage.js.map +1 -0
  5. package/dist/auth/token-manager.d.ts +39 -0
  6. package/dist/auth/token-manager.d.ts.map +1 -0
  7. package/dist/auth/token-manager.js +98 -0
  8. package/dist/auth/token-manager.js.map +1 -0
  9. package/dist/client.d.ts +31 -0
  10. package/dist/client.d.ts.map +1 -0
  11. package/dist/client.js +39 -0
  12. package/dist/client.js.map +1 -0
  13. package/dist/http/errors.d.ts +51 -0
  14. package/dist/http/errors.d.ts.map +1 -0
  15. package/dist/http/errors.js +92 -0
  16. package/dist/http/errors.js.map +1 -0
  17. package/dist/http/http-client.d.ts +18 -0
  18. package/dist/http/http-client.d.ts.map +1 -0
  19. package/dist/http/http-client.js +221 -0
  20. package/dist/http/http-client.js.map +1 -0
  21. package/dist/http/types.d.ts +27 -0
  22. package/dist/http/types.d.ts.map +1 -0
  23. package/dist/http/types.js +3 -0
  24. package/dist/http/types.js.map +1 -0
  25. package/dist/index.d.ts +18 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +36 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/modules/accounts.module.d.ts +15 -0
  30. package/dist/modules/accounts.module.d.ts.map +1 -0
  31. package/dist/modules/accounts.module.js +57 -0
  32. package/dist/modules/accounts.module.js.map +1 -0
  33. package/dist/modules/analytics.module.d.ts +14 -0
  34. package/dist/modules/analytics.module.d.ts.map +1 -0
  35. package/dist/modules/analytics.module.js +24 -0
  36. package/dist/modules/analytics.module.js.map +1 -0
  37. package/dist/modules/auth.module.d.ts +18 -0
  38. package/dist/modules/auth.module.d.ts.map +1 -0
  39. package/dist/modules/auth.module.js +113 -0
  40. package/dist/modules/auth.module.js.map +1 -0
  41. package/dist/modules/content-plans.module.d.ts +58 -0
  42. package/dist/modules/content-plans.module.d.ts.map +1 -0
  43. package/dist/modules/content-plans.module.js +125 -0
  44. package/dist/modules/content-plans.module.js.map +1 -0
  45. package/dist/modules/customers.module.d.ts +12 -0
  46. package/dist/modules/customers.module.d.ts.map +1 -0
  47. package/dist/modules/customers.module.js +43 -0
  48. package/dist/modules/customers.module.js.map +1 -0
  49. package/dist/modules/notifications.module.d.ts +20 -0
  50. package/dist/modules/notifications.module.d.ts.map +1 -0
  51. package/dist/modules/notifications.module.js +37 -0
  52. package/dist/modules/notifications.module.js.map +1 -0
  53. package/dist/modules/orgs.module.d.ts +23 -0
  54. package/dist/modules/orgs.module.d.ts.map +1 -0
  55. package/dist/modules/orgs.module.js +112 -0
  56. package/dist/modules/orgs.module.js.map +1 -0
  57. package/dist/modules/projects.module.d.ts +26 -0
  58. package/dist/modules/projects.module.d.ts.map +1 -0
  59. package/dist/modules/projects.module.js +88 -0
  60. package/dist/modules/projects.module.js.map +1 -0
  61. package/dist/modules/scheduling.module.d.ts +13 -0
  62. package/dist/modules/scheduling.module.d.ts.map +1 -0
  63. package/dist/modules/scheduling.module.js +32 -0
  64. package/dist/modules/scheduling.module.js.map +1 -0
  65. package/dist/modules/storage.module.d.ts +36 -0
  66. package/dist/modules/storage.module.d.ts.map +1 -0
  67. package/dist/modules/storage.module.js +100 -0
  68. package/dist/modules/storage.module.js.map +1 -0
  69. package/dist/modules/subscriptions.module.d.ts +25 -0
  70. package/dist/modules/subscriptions.module.d.ts.map +1 -0
  71. package/dist/modules/subscriptions.module.js +62 -0
  72. package/dist/modules/subscriptions.module.js.map +1 -0
  73. package/dist/types/account.types.d.ts +17 -0
  74. package/dist/types/account.types.d.ts.map +1 -0
  75. package/dist/types/account.types.js +3 -0
  76. package/dist/types/account.types.js.map +1 -0
  77. package/dist/types/analytics.types.d.ts +15 -0
  78. package/dist/types/analytics.types.d.ts.map +1 -0
  79. package/dist/types/analytics.types.js +3 -0
  80. package/dist/types/analytics.types.js.map +1 -0
  81. package/dist/types/api.types.d.ts +20 -0
  82. package/dist/types/api.types.d.ts.map +1 -0
  83. package/dist/types/api.types.js +3 -0
  84. package/dist/types/api.types.js.map +1 -0
  85. package/dist/types/auth.types.d.ts +40 -0
  86. package/dist/types/auth.types.d.ts.map +1 -0
  87. package/dist/types/auth.types.js +3 -0
  88. package/dist/types/auth.types.js.map +1 -0
  89. package/dist/types/content-plan.types.d.ts +27 -0
  90. package/dist/types/content-plan.types.d.ts.map +1 -0
  91. package/dist/types/content-plan.types.js +3 -0
  92. package/dist/types/content-plan.types.js.map +1 -0
  93. package/dist/types/customer.types.d.ts +25 -0
  94. package/dist/types/customer.types.d.ts.map +1 -0
  95. package/dist/types/customer.types.js +3 -0
  96. package/dist/types/customer.types.js.map +1 -0
  97. package/dist/types/index.d.ts +585 -0
  98. package/dist/types/index.d.ts.map +1 -0
  99. package/dist/types/index.js +6 -0
  100. package/dist/types/index.js.map +1 -0
  101. package/dist/types/notification.types.d.ts +11 -0
  102. package/dist/types/notification.types.d.ts.map +1 -0
  103. package/dist/types/notification.types.js +3 -0
  104. package/dist/types/notification.types.js.map +1 -0
  105. package/dist/types/org.types.d.ts +25 -0
  106. package/dist/types/org.types.d.ts.map +1 -0
  107. package/dist/types/org.types.js +3 -0
  108. package/dist/types/org.types.js.map +1 -0
  109. package/dist/types/project.types.d.ts +20 -0
  110. package/dist/types/project.types.d.ts.map +1 -0
  111. package/dist/types/project.types.js +3 -0
  112. package/dist/types/project.types.js.map +1 -0
  113. package/dist/types/scheduling.types.d.ts +31 -0
  114. package/dist/types/scheduling.types.d.ts.map +1 -0
  115. package/dist/types/scheduling.types.js +3 -0
  116. package/dist/types/scheduling.types.js.map +1 -0
  117. package/dist/types/storage.types.d.ts +8 -0
  118. package/dist/types/storage.types.d.ts.map +1 -0
  119. package/dist/types/storage.types.js +3 -0
  120. package/dist/types/storage.types.js.map +1 -0
  121. package/dist/types/subscription.types.d.ts +19 -0
  122. package/dist/types/subscription.types.d.ts.map +1 -0
  123. package/dist/types/subscription.types.js +3 -0
  124. package/dist/types/subscription.types.js.map +1 -0
  125. package/package.json +27 -0
  126. package/src/auth/storage.ts +75 -0
  127. package/src/auth/token-manager.ts +107 -0
  128. package/src/client.ts +51 -0
  129. package/src/http/errors.ts +97 -0
  130. package/src/http/http-client.ts +263 -0
  131. package/src/http/types.ts +32 -0
  132. package/src/index.ts +17 -0
  133. package/src/modules/accounts.module.ts +61 -0
  134. package/src/modules/analytics.module.ts +26 -0
  135. package/src/modules/auth.module.ts +129 -0
  136. package/src/modules/content-plans.module.ts +170 -0
  137. package/src/modules/customers.module.ts +48 -0
  138. package/src/modules/notifications.module.ts +41 -0
  139. package/src/modules/orgs.module.ts +122 -0
  140. package/src/modules/projects.module.ts +97 -0
  141. package/src/modules/scheduling.module.ts +35 -0
  142. package/src/modules/storage.module.ts +121 -0
  143. package/src/modules/subscriptions.module.ts +82 -0
  144. package/src/types/account.types.ts +17 -0
  145. package/src/types/analytics.types.ts +15 -0
  146. package/src/types/api.types.ts +21 -0
  147. package/src/types/auth.types.ts +45 -0
  148. package/src/types/content-plan.types.ts +28 -0
  149. package/src/types/customer.types.ts +25 -0
  150. package/src/types/index.ts +682 -0
  151. package/src/types/notification.types.ts +10 -0
  152. package/src/types/org.types.ts +27 -0
  153. package/src/types/project.types.ts +21 -0
  154. package/src/types/scheduling.types.ts +32 -0
  155. package/src/types/storage.types.ts +7 -0
  156. package/src/types/subscription.types.ts +19 -0
@@ -0,0 +1,263 @@
1
+ import { AddnClientOptions, HttpRequestOptions } from "./types";
2
+ import { ApiResponse, ValidationErrorDetail } from "../types";
3
+ import {
4
+ AddnError,
5
+ AddnNetworkError,
6
+ AddnTimeoutError,
7
+ AddnApiError,
8
+ AddnAuthError,
9
+ AddnForbiddenError,
10
+ AddnNotFoundError,
11
+ AddnConflictError,
12
+ AddnValidationError,
13
+ AddnRateLimitError,
14
+ AddnServerError,
15
+ AddnUnexpectedError,
16
+ } from "./errors";
17
+ import { TokenManager, TokenPair } from "../auth/token-manager";
18
+ import { LocalStorageAdapter } from "../auth/storage";
19
+
20
+ export class HttpClient {
21
+ private readonly baseUrl: string;
22
+ private readonly timeoutMs: number;
23
+ public readonly tokenManager: TokenManager;
24
+ private readonly onRequest?: AddnClientOptions["onRequest"];
25
+ private readonly onError?: AddnClientOptions["onError"];
26
+ private readonly onUnauthenticated?: AddnClientOptions["onUnauthenticated"];
27
+
28
+ constructor(options: AddnClientOptions) {
29
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
30
+ this.timeoutMs = options.timeoutMs ?? 30000;
31
+ this.onRequest = options.onRequest;
32
+ this.onError = options.onError;
33
+ this.onUnauthenticated = options.onUnauthenticated;
34
+
35
+ const storage = options.storage ?? new LocalStorageAdapter();
36
+ this.tokenManager = new TokenManager(storage, this.onUnauthenticated);
37
+ }
38
+
39
+ public async request<T = any>(options: HttpRequestOptions): Promise<T> {
40
+ let opts = { ...options };
41
+
42
+ if (this.onRequest) {
43
+ try {
44
+ opts = await this.onRequest(opts);
45
+ } catch (err) {
46
+ throw new AddnUnexpectedError("Custom onRequest hook failed", err);
47
+ }
48
+ }
49
+
50
+ try {
51
+ return await this.executeRequest<T>(opts);
52
+ } catch (error) {
53
+ if (error instanceof AddnAuthError && opts.requiresAuth !== false) {
54
+ // Attempt token refresh
55
+ const newTokens = await this.tokenManager.handleRefresh(async (refreshToken) => {
56
+ return await this.rawRefreshTokenCall(refreshToken);
57
+ });
58
+
59
+ if (newTokens) {
60
+ // Retry request with new token
61
+ try {
62
+ return await this.executeRequest<T>(opts);
63
+ } catch (retryErr) {
64
+ this.handleErrorTrigger(retryErr);
65
+ throw retryErr;
66
+ }
67
+ }
68
+ }
69
+
70
+ this.handleErrorTrigger(error);
71
+ throw error;
72
+ }
73
+ }
74
+
75
+ private async executeRequest<T>(options: HttpRequestOptions): Promise<T> {
76
+ const method = options.method ?? "GET";
77
+ const path = options.path.startsWith("/") ? options.path : `/${options.path}`;
78
+ const url = this.buildUrl(path, options.query);
79
+
80
+ const headers: Record<string, string> = { ...options.headers };
81
+
82
+ if (options.requiresAuth !== false) {
83
+ const token = await this.tokenManager.getAccessToken();
84
+ if (token && !headers["Authorization"]) {
85
+ headers["Authorization"] = `Bearer ${token}`;
86
+ }
87
+ }
88
+
89
+ let body: any = undefined;
90
+ if (options.formData) {
91
+ body = options.formData;
92
+ } else if (options.body !== undefined && options.body !== null) {
93
+ if (!headers["Content-Type"]) {
94
+ headers["Content-Type"] = "application/json";
95
+ }
96
+ body = JSON.stringify(options.body);
97
+ }
98
+
99
+ const controller = new AbortController();
100
+ const timeoutMs = options.timeoutMs ?? this.timeoutMs;
101
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
102
+
103
+ // Merge external signal with timeout signal if provided
104
+ if (options.signal) {
105
+ options.signal.addEventListener("abort", () => controller.abort());
106
+ }
107
+
108
+ let response: Response;
109
+ try {
110
+ response = await fetch(url, {
111
+ method,
112
+ headers,
113
+ body,
114
+ signal: controller.signal,
115
+ credentials: "include",
116
+ });
117
+ } catch (err: unknown) {
118
+ if (
119
+ (err instanceof Error && err.name === "AbortError") ||
120
+ (typeof DOMException !== "undefined" && err instanceof DOMException && err.name === "AbortError") ||
121
+ controller.signal.aborted
122
+ ) {
123
+ throw new AddnTimeoutError(timeoutMs);
124
+ }
125
+ const msg = err instanceof Error ? err.message : "Network request failed";
126
+ throw new AddnNetworkError(msg, err);
127
+ } finally {
128
+ clearTimeout(timeoutId);
129
+ }
130
+
131
+ return await this.parseResponse<T>(response);
132
+ }
133
+
134
+ private async parseResponse<T>(response: Response): Promise<T> {
135
+ const contentType = response.headers.get("content-type") || "";
136
+ let data: any = null;
137
+ let rawText = "";
138
+
139
+ try {
140
+ if (contentType.includes("application/json")) {
141
+ data = await response.json();
142
+ } else {
143
+ rawText = await response.text();
144
+ if (rawText) {
145
+ try {
146
+ data = JSON.parse(rawText);
147
+ } catch {
148
+ data = { message: rawText };
149
+ }
150
+ }
151
+ }
152
+ } catch (parseErr) {
153
+ if (!response.ok) {
154
+ throw new AddnUnexpectedError(`HTTP ${response.status} ${response.statusText} - Failed to parse response body`, parseErr);
155
+ }
156
+ return null as unknown as T;
157
+ }
158
+
159
+ if (response.ok) {
160
+ // Check if response conforms to standard ApiResponse envelope { success: true, data: T }
161
+ if (data && typeof data === "object" && "data" in data && "success" in data) {
162
+ return data.data as T;
163
+ }
164
+ return data as T;
165
+ }
166
+
167
+ // Handle Error Statuses
168
+ const message = data?.message || data?.detail || response.statusText || `Request failed with status ${response.status}`;
169
+ const errorCode = data?.code || "API_ERROR";
170
+ const statusCode = response.status;
171
+
172
+ if (statusCode === 401) {
173
+ throw new AddnAuthError(message, statusCode, errorCode, data?.details);
174
+ }
175
+
176
+ if (statusCode === 403) {
177
+ throw new AddnForbiddenError(message, statusCode, errorCode, data?.details);
178
+ }
179
+
180
+ if (statusCode === 404) {
181
+ throw new AddnNotFoundError(message, statusCode, errorCode, data?.details);
182
+ }
183
+
184
+ if (statusCode === 409) {
185
+ throw new AddnConflictError(message, statusCode, errorCode, data?.details);
186
+ }
187
+
188
+ if (statusCode === 422) {
189
+ const details: ValidationErrorDetail[] = Array.isArray(data?.details)
190
+ ? data.details.map((d: any) => ({
191
+ field: d.field || d.loc?.join(".") || "payload",
192
+ message: d.message || d.msg || "Invalid value",
193
+ type: d.type,
194
+ }))
195
+ : [];
196
+ throw new AddnValidationError(message, details, data);
197
+ }
198
+
199
+ if (statusCode === 429) {
200
+ const retryHeader = response.headers.get("Retry-After");
201
+ const retrySecs = retryHeader ? parseInt(retryHeader, 10) : undefined;
202
+ throw new AddnRateLimitError(message, retrySecs);
203
+ }
204
+
205
+ if (statusCode >= 500) {
206
+ throw new AddnServerError(message, statusCode, errorCode, data?.details);
207
+ }
208
+
209
+ throw new AddnApiError(message, statusCode, errorCode, data?.details, data);
210
+ }
211
+
212
+ private async rawRefreshTokenCall(refreshToken?: string): Promise<TokenPair> {
213
+ const url = `${this.baseUrl}/api/v1/auth/refresh`;
214
+ const response = await fetch(url, {
215
+ method: "POST",
216
+ headers: { "Content-Type": "application/json" },
217
+ body: JSON.stringify(refreshToken ? { refresh_token: refreshToken } : {}),
218
+ credentials: "include",
219
+ });
220
+
221
+ if (!response.ok) {
222
+ throw new AddnAuthError("Refresh token rejected", response.status);
223
+ }
224
+
225
+ const data = await response.json();
226
+ const tokenData = data.data || data;
227
+
228
+ if (!tokenData.access_token) {
229
+ throw new AddnAuthError("Refresh endpoint returned invalid token payload");
230
+ }
231
+
232
+ return {
233
+ accessToken: tokenData.access_token,
234
+ refreshToken: tokenData.refresh_token,
235
+ };
236
+ }
237
+
238
+ private buildUrl(path: string, query?: Record<string, string | number | boolean | (string | number)[] | undefined | null>): string {
239
+ const fullUrl = new URL(`${this.baseUrl}${path}`);
240
+ if (query) {
241
+ Object.entries(query).forEach(([key, value]) => {
242
+ if (value !== undefined && value !== null) {
243
+ if (Array.isArray(value)) {
244
+ value.forEach((v) => fullUrl.searchParams.append(key, String(v)));
245
+ } else {
246
+ fullUrl.searchParams.append(key, String(value));
247
+ }
248
+ }
249
+ });
250
+ }
251
+ return fullUrl.toString();
252
+ }
253
+
254
+ private handleErrorTrigger(error: unknown): void {
255
+ if (this.onError && error instanceof AddnError) {
256
+ try {
257
+ this.onError(error);
258
+ } catch {
259
+ // Prevent onError callback exceptions from masking original error
260
+ }
261
+ }
262
+ }
263
+ }
@@ -0,0 +1,32 @@
1
+ import { AddnError } from "./errors";
2
+
3
+ export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
4
+
5
+ export interface HttpRequestOptions {
6
+ path: string;
7
+ method?: HttpMethod;
8
+ headers?: Record<string, string>;
9
+ query?: Record<string, string | number | boolean | (string | number)[] | undefined | null>;
10
+ body?: unknown;
11
+ formData?: FormData;
12
+ requiresAuth?: boolean;
13
+ timeoutMs?: number;
14
+ signal?: AbortSignal;
15
+ }
16
+
17
+ import { ApiResponse, PaginatedMeta, PaginatedResponse } from "../types";
18
+
19
+ export interface StorageAdapter {
20
+ getItem(key: string): string | null | Promise<string | null>;
21
+ setItem(key: string, value: string): void | Promise<void>;
22
+ removeItem(key: string): void | Promise<void>;
23
+ }
24
+
25
+ export interface AddnClientOptions {
26
+ baseUrl: string;
27
+ timeoutMs?: number;
28
+ storage?: StorageAdapter;
29
+ onUnauthenticated?: () => void;
30
+ onRequest?: (options: HttpRequestOptions) => HttpRequestOptions | Promise<HttpRequestOptions>;
31
+ onError?: (error: AddnError) => void;
32
+ }
package/src/index.ts ADDED
@@ -0,0 +1,17 @@
1
+ export { AddnClient } from "./client";
2
+ export * from "./http/types";
3
+ export * from "./http/errors";
4
+ export * from "./auth/storage";
5
+ export * from "./auth/token-manager";
6
+ export * from "./modules/auth.module";
7
+ export * from "./modules/orgs.module";
8
+ export * from "./modules/customers.module";
9
+ export * from "./modules/projects.module";
10
+ export * from "./modules/content-plans.module";
11
+ export * from "./modules/scheduling.module";
12
+ export * from "./modules/accounts.module";
13
+ export * from "./modules/analytics.module";
14
+ export * from "./modules/notifications.module";
15
+ export * from "./modules/storage.module";
16
+ export * from "./modules/subscriptions.module";
17
+ export * from "./types";
@@ -0,0 +1,61 @@
1
+ import { HttpClient } from "../http/http-client";
2
+ import type { ConnectedAccount, ConnectAccountPayload, DevConnectPayload } from "../types";
3
+
4
+ export class AccountsModule {
5
+ private readonly http: HttpClient;
6
+
7
+ constructor(http: HttpClient) {
8
+ this.http = http;
9
+ }
10
+
11
+ public async listConnectedAccounts(orgId?: string): Promise<ConnectedAccount[]> {
12
+ const res = await this.http.request<any>({
13
+ path: "/api/v1/accounts",
14
+ method: "GET",
15
+ query: orgId ? { org_id: orgId } : undefined,
16
+ });
17
+ if (Array.isArray(res)) return res;
18
+ if (res?.accounts && Array.isArray(res.accounts)) return res.accounts;
19
+ return [];
20
+ }
21
+
22
+ public async getConnectUrl(method: "facebook" | "instagram" = "facebook"): Promise<{ url: string }> {
23
+ return await this.http.request<{ url: string }>({
24
+ path: `/api/v1/account/connect?login_method=${method}`,
25
+ method: "POST",
26
+ body: {},
27
+ });
28
+ }
29
+
30
+ public async devConnectAccount(payload: DevConnectPayload): Promise<ConnectedAccount> {
31
+ const res = await this.http.request<any>({
32
+ path: "/api/v1/account/dev-connect",
33
+ method: "POST",
34
+ body: payload,
35
+ });
36
+ return res?.account || res;
37
+ }
38
+
39
+ public async connectAccount(payload: ConnectAccountPayload): Promise<ConnectedAccount> {
40
+ return await this.http.request<ConnectedAccount>({
41
+ path: "/api/v1/account/connect",
42
+ method: "POST",
43
+ body: payload,
44
+ });
45
+ }
46
+
47
+ public async disconnectAccount(accountId: string): Promise<void> {
48
+ await this.http.request({
49
+ path: `/api/v1/account/${accountId}`,
50
+ method: "DELETE",
51
+ });
52
+ }
53
+
54
+ public async refreshAccountToken(accountId: string): Promise<any> {
55
+ return await this.http.request<any>({
56
+ path: `/api/v1/account/${accountId}/refresh-token`,
57
+ method: "POST",
58
+ body: {},
59
+ });
60
+ }
61
+ }
@@ -0,0 +1,26 @@
1
+ import { HttpClient } from "../http/http-client";
2
+ import type { AnalyticsSummary, InstagramAnalyticsData } from "../types";
3
+
4
+ export class AnalyticsModule {
5
+ private readonly http: HttpClient;
6
+
7
+ constructor(http: HttpClient) {
8
+ this.http = http;
9
+ }
10
+
11
+ public async getSummary(params: { org_id?: string; project_id?: string; start_date?: string; end_date?: string }): Promise<AnalyticsSummary> {
12
+ return await this.http.request<AnalyticsSummary>({
13
+ path: "/api/v1/analytics/summary",
14
+ method: "GET",
15
+ query: params,
16
+ });
17
+ }
18
+
19
+ public async getInstagramAnalytics(accountId: string, timeframe: "7d" | "30d" | "90d" = "30d"): Promise<InstagramAnalyticsData> {
20
+ return await this.http.request<InstagramAnalyticsData>({
21
+ path: `/api/v1/analytics/instagram/${accountId}`,
22
+ method: "GET",
23
+ query: { timeframe },
24
+ });
25
+ }
26
+ }
@@ -0,0 +1,129 @@
1
+ import { HttpClient } from "../http/http-client";
2
+ import type {
3
+ AuthTokenResponse,
4
+ LoginPayload,
5
+ RegisterPayload,
6
+ UserProfile,
7
+ PasswordResetRequestPayload,
8
+ PasswordResetConfirmPayload,
9
+ MetaLoginPayload,
10
+ } from "../types";
11
+
12
+ export class AuthModule {
13
+ private readonly http: HttpClient;
14
+
15
+ constructor(http: HttpClient) {
16
+ this.http = http;
17
+ }
18
+
19
+ public async login(payload: LoginPayload): Promise<AuthTokenResponse> {
20
+ const res = await this.http.request<AuthTokenResponse>({
21
+ path: "/api/v1/auth/login",
22
+ method: "POST",
23
+ body: {
24
+ email: payload.email || payload.username,
25
+ password: payload.password,
26
+ },
27
+ requiresAuth: false,
28
+ });
29
+ if (res?.access_token && res?.refresh_token) {
30
+ await this.http.tokenManager.setTokens(res.access_token, res.refresh_token);
31
+ }
32
+ if (res?.user) {
33
+ await this.http.tokenManager.setUser(res.user);
34
+ }
35
+ return res;
36
+ }
37
+
38
+ public async register(payload: RegisterPayload): Promise<AuthTokenResponse> {
39
+ const res = await this.http.request<AuthTokenResponse>({
40
+ path: "/api/v1/auth/register",
41
+ method: "POST",
42
+ body: payload,
43
+ requiresAuth: false,
44
+ });
45
+ if (res?.access_token && res?.refresh_token) {
46
+ await this.http.tokenManager.setTokens(res.access_token, res.refresh_token);
47
+ }
48
+ if (res?.user) {
49
+ await this.http.tokenManager.setUser(res.user);
50
+ }
51
+ return res;
52
+ }
53
+
54
+ public async getMe(): Promise<UserProfile> {
55
+ const user = await this.http.request<UserProfile>({
56
+ path: "/api/v1/auth/me",
57
+ method: "GET",
58
+ });
59
+ if (user) {
60
+ await this.http.tokenManager.setUser(user);
61
+ }
62
+ return user;
63
+ }
64
+
65
+ public async logout(): Promise<void> {
66
+ try {
67
+ await this.http.request({
68
+ path: "/api/v1/auth/logout",
69
+ method: "POST",
70
+ });
71
+ } finally {
72
+ await this.http.tokenManager.clearTokens();
73
+ }
74
+ }
75
+
76
+ public async requestPasswordReset(payload: PasswordResetRequestPayload): Promise<void> {
77
+ await this.http.request({
78
+ path: "/api/v1/auth/forgot-password",
79
+ method: "POST",
80
+ body: payload,
81
+ requiresAuth: false,
82
+ });
83
+ }
84
+
85
+ public async confirmPasswordReset(payload: PasswordResetConfirmPayload): Promise<void> {
86
+ await this.http.request({
87
+ path: "/api/v1/auth/reset-password",
88
+ method: "POST",
89
+ body: payload,
90
+ requiresAuth: false,
91
+ });
92
+ }
93
+
94
+ public async completeOnboarding(): Promise<UserProfile> {
95
+ const user = await this.http.request<UserProfile>({
96
+ path: "/api/v1/auth/onboarding/complete",
97
+ method: "PATCH",
98
+ });
99
+ if (user) {
100
+ await this.http.tokenManager.setUser(user);
101
+ }
102
+ return user;
103
+ }
104
+
105
+ public async deleteAccount(): Promise<{ message: string }> {
106
+ const res = await this.http.request<{ message: string }>({
107
+ path: "/api/v1/auth/account",
108
+ method: "DELETE",
109
+ });
110
+ await this.http.tokenManager.clearTokens();
111
+ return res;
112
+ }
113
+
114
+ public async metaLogin(payload: MetaLoginPayload): Promise<AuthTokenResponse> {
115
+ const res = await this.http.request<AuthTokenResponse>({
116
+ path: "/api/v1/auth/meta/callback",
117
+ method: "POST",
118
+ body: payload,
119
+ requiresAuth: false,
120
+ });
121
+ if (res?.access_token && res?.refresh_token) {
122
+ await this.http.tokenManager.setTokens(res.access_token, res.refresh_token);
123
+ }
124
+ if (res?.user) {
125
+ await this.http.tokenManager.setUser(res.user);
126
+ }
127
+ return res;
128
+ }
129
+ }
@@ -0,0 +1,170 @@
1
+ import { HttpClient } from "../http/http-client";
2
+ import type { ContentPlan, CreateContentPlanPayload, UpdateContentPlanPayload } from "../types";
3
+
4
+ export class ContentPlansModule {
5
+ private readonly http: HttpClient;
6
+
7
+ constructor(http: HttpClient) {
8
+ this.http = http;
9
+ }
10
+
11
+ public async listContentPlans(params?: {
12
+ project_id?: string;
13
+ status?: string;
14
+ instagram_account_id?: string;
15
+ }): Promise<ContentPlan[]> {
16
+ const res = await this.http.request<any>({
17
+ path: "/api/v1/content-plans",
18
+ method: "GET",
19
+ query: params,
20
+ });
21
+ if (res && Array.isArray(res.content_plans)) return res.content_plans;
22
+ if (Array.isArray(res)) return res;
23
+ return [];
24
+ }
25
+
26
+ public async getContentPlan(planId: string): Promise<ContentPlan> {
27
+ const res = await this.http.request<any>({
28
+ path: `/api/v1/content-plans/${planId}`,
29
+ method: "GET",
30
+ });
31
+ return res?.content_plan || res;
32
+ }
33
+
34
+ public async createContentPlan(payload: CreateContentPlanPayload): Promise<ContentPlan> {
35
+ const res = await this.http.request<any>({
36
+ path: "/api/v1/content-plans",
37
+ method: "POST",
38
+ body: payload,
39
+ });
40
+ return res?.content_plan || res;
41
+ }
42
+
43
+ public async updateContentPlan(planId: string, payload: UpdateContentPlanPayload): Promise<ContentPlan> {
44
+ const res = await this.http.request<any>({
45
+ path: `/api/v1/content-plans/${planId}`,
46
+ method: "PATCH",
47
+ body: payload,
48
+ });
49
+ return res?.content_plan || res;
50
+ }
51
+
52
+ public async deleteContentPlan(planId: string): Promise<void> {
53
+ await this.http.request({
54
+ path: `/api/v1/content-plans/${planId}`,
55
+ method: "DELETE",
56
+ });
57
+ }
58
+
59
+ public async submitForReview(planId: string, approverIds: string[], note?: string): Promise<ContentPlan> {
60
+ const res = await this.http.request<any>({
61
+ path: `/api/v1/content-plans/${planId}/submit`,
62
+ method: "POST",
63
+ body: { approver_ids: approverIds, note },
64
+ });
65
+ return res?.content_plan || res;
66
+ }
67
+
68
+ public async approveDirectly(planId: string, note?: string): Promise<ContentPlan> {
69
+ const res = await this.http.request<any>({
70
+ path: `/api/v1/content-plans/${planId}/approve-directly`,
71
+ method: "POST",
72
+ body: { note },
73
+ });
74
+ return res?.content_plan || res;
75
+ }
76
+
77
+ public async review(planId: string, action: "approved" | "changes_requested", note?: string): Promise<ContentPlan> {
78
+ const res = await this.http.request<any>({
79
+ path: `/api/v1/content-plans/${planId}/review`,
80
+ method: "POST",
81
+ body: { action, note },
82
+ });
83
+ return res?.content_plan || res;
84
+ }
85
+
86
+ public async generateContent(
87
+ planId: string,
88
+ outputType: "idea" | "script" | "caption" | "hashtags" | "visual_direction",
89
+ instructions?: string
90
+ ): Promise<{ output_type: string; content: string }> {
91
+ return await this.http.request<{ output_type: string; content: string }>({
92
+ path: `/api/v1/content-plans/${planId}/generate`,
93
+ method: "POST",
94
+ body: { output_type: outputType, instructions },
95
+ });
96
+ }
97
+
98
+ public async generateStandalone(payload: {
99
+ output_type: string;
100
+ instructions: string;
101
+ title?: string;
102
+ objective?: string;
103
+ core_idea?: string;
104
+ format?: string;
105
+ }): Promise<{ output_type: string; content: string }> {
106
+ return await this.http.request<{ output_type: string; content: string }>({
107
+ path: "/api/v1/content-plans/generate-ai-field",
108
+ method: "POST",
109
+ body: payload,
110
+ });
111
+ }
112
+
113
+ public async generateFullPlan(payload: {
114
+ content_type: string;
115
+ prompt: string;
116
+ project_id?: string;
117
+ }): Promise<{
118
+ plan: {
119
+ title?: string;
120
+ objective?: string;
121
+ core_idea?: string;
122
+ hook?: string;
123
+ script?: string;
124
+ call_to_action?: string;
125
+ visual_direction?: string;
126
+ caption?: string;
127
+ hashtags?: string;
128
+ content_pillar?: string;
129
+ };
130
+ }> {
131
+ return await this.http.request<any>({
132
+ path: "/api/v1/content-plans/generate-full-plan",
133
+ method: "POST",
134
+ body: payload,
135
+ });
136
+ }
137
+
138
+ public async sharePlan(planId: string, message?: string): Promise<{ recipient_count: number }> {
139
+ return await this.http.request<{ recipient_count: number }>({
140
+ path: `/api/v1/content-plans/${planId}/share`,
141
+ method: "POST",
142
+ body: { message },
143
+ });
144
+ }
145
+
146
+ public async getComments(planId: string): Promise<any[]> {
147
+ const res = await this.http.request<any>({
148
+ path: `/api/v1/content-plans/${planId}/comments`,
149
+ method: "GET",
150
+ });
151
+ return res?.comments || [];
152
+ }
153
+
154
+ public async addComment(planId: string, body: string): Promise<any> {
155
+ const res = await this.http.request<any>({
156
+ path: `/api/v1/content-plans/${planId}/comments`,
157
+ method: "POST",
158
+ body: { body },
159
+ });
160
+ return res?.comment || res;
161
+ }
162
+
163
+ public async getAudit(planId: string): Promise<any[]> {
164
+ const res = await this.http.request<any>({
165
+ path: `/api/v1/content-plans/${planId}/audit`,
166
+ method: "GET",
167
+ });
168
+ return res?.events || [];
169
+ }
170
+ }