@kendew-agency/clickup-sdk 0.1.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.
Files changed (119) hide show
  1. package/README.md +545 -0
  2. package/dist/clickup.d.ts +37 -0
  3. package/dist/clickup.d.ts.map +1 -0
  4. package/dist/clickup.js +44 -0
  5. package/dist/clickup.js.map +1 -0
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +3 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/interfaces.d.ts +14 -0
  11. package/dist/interfaces.d.ts.map +1 -0
  12. package/dist/interfaces.js +2 -0
  13. package/dist/interfaces.js.map +1 -0
  14. package/dist/methods/attachments/attachments.d.ts +13 -0
  15. package/dist/methods/attachments/attachments.d.ts.map +1 -0
  16. package/dist/methods/attachments/attachments.js +27 -0
  17. package/dist/methods/attachments/attachments.js.map +1 -0
  18. package/dist/methods/attachments/types.d.ts +28 -0
  19. package/dist/methods/attachments/types.d.ts.map +1 -0
  20. package/dist/methods/attachments/types.js +2 -0
  21. package/dist/methods/attachments/types.js.map +1 -0
  22. package/dist/methods/authorization/authorization.d.ts +18 -0
  23. package/dist/methods/authorization/authorization.d.ts.map +1 -0
  24. package/dist/methods/authorization/authorization.js +28 -0
  25. package/dist/methods/authorization/authorization.js.map +1 -0
  26. package/dist/methods/authorization/types.d.ts +13 -0
  27. package/dist/methods/authorization/types.d.ts.map +1 -0
  28. package/dist/methods/authorization/types.js +2 -0
  29. package/dist/methods/authorization/types.js.map +1 -0
  30. package/dist/methods/base.d.ts +161 -0
  31. package/dist/methods/base.d.ts.map +1 -0
  32. package/dist/methods/base.js +314 -0
  33. package/dist/methods/base.js.map +1 -0
  34. package/dist/methods/comments/comments.d.ts +94 -0
  35. package/dist/methods/comments/comments.d.ts.map +1 -0
  36. package/dist/methods/comments/comments.js +158 -0
  37. package/dist/methods/comments/comments.js.map +1 -0
  38. package/dist/methods/comments/types.d.ts +70 -0
  39. package/dist/methods/comments/types.d.ts.map +1 -0
  40. package/dist/methods/comments/types.js +2 -0
  41. package/dist/methods/comments/types.js.map +1 -0
  42. package/dist/methods/custom-fields/custom-fields.d.ts +32 -0
  43. package/dist/methods/custom-fields/custom-fields.d.ts.map +1 -0
  44. package/dist/methods/custom-fields/custom-fields.js +43 -0
  45. package/dist/methods/custom-fields/custom-fields.js.map +1 -0
  46. package/dist/methods/custom-fields/types.d.ts +58 -0
  47. package/dist/methods/custom-fields/types.d.ts.map +1 -0
  48. package/dist/methods/custom-fields/types.js +2 -0
  49. package/dist/methods/custom-fields/types.js.map +1 -0
  50. package/dist/methods/custom-task-types/custom-task-types.d.ts +12 -0
  51. package/dist/methods/custom-task-types/custom-task-types.d.ts.map +1 -0
  52. package/dist/methods/custom-task-types/custom-task-types.js +15 -0
  53. package/dist/methods/custom-task-types/custom-task-types.js.map +1 -0
  54. package/dist/methods/custom-task-types/types.d.ts +15 -0
  55. package/dist/methods/custom-task-types/types.d.ts.map +1 -0
  56. package/dist/methods/custom-task-types/types.js +2 -0
  57. package/dist/methods/custom-task-types/types.js.map +1 -0
  58. package/dist/methods/folders/folders.d.ts +211 -0
  59. package/dist/methods/folders/folders.d.ts.map +1 -0
  60. package/dist/methods/folders/folders.js +80 -0
  61. package/dist/methods/folders/folders.js.map +1 -0
  62. package/dist/methods/folders/types.d.ts +71 -0
  63. package/dist/methods/folders/types.d.ts.map +1 -0
  64. package/dist/methods/folders/types.js +2 -0
  65. package/dist/methods/folders/types.js.map +1 -0
  66. package/dist/methods/goals/goals.d.ts +51 -0
  67. package/dist/methods/goals/goals.d.ts.map +1 -0
  68. package/dist/methods/goals/goals.js +86 -0
  69. package/dist/methods/goals/goals.js.map +1 -0
  70. package/dist/methods/goals/types.d.ts +92 -0
  71. package/dist/methods/goals/types.d.ts.map +1 -0
  72. package/dist/methods/goals/types.js +2 -0
  73. package/dist/methods/goals/types.js.map +1 -0
  74. package/dist/methods/lists/lists.d.ts +252 -0
  75. package/dist/methods/lists/lists.d.ts.map +1 -0
  76. package/dist/methods/lists/lists.js +132 -0
  77. package/dist/methods/lists/lists.js.map +1 -0
  78. package/dist/methods/lists/types.d.ts +91 -0
  79. package/dist/methods/lists/types.d.ts.map +1 -0
  80. package/dist/methods/lists/types.js +2 -0
  81. package/dist/methods/lists/types.js.map +1 -0
  82. package/dist/methods/spaces/spaces.d.ts +187 -0
  83. package/dist/methods/spaces/spaces.d.ts.map +1 -0
  84. package/dist/methods/spaces/spaces.js +87 -0
  85. package/dist/methods/spaces/spaces.js.map +1 -0
  86. package/dist/methods/spaces/types.d.ts +71 -0
  87. package/dist/methods/spaces/types.d.ts.map +1 -0
  88. package/dist/methods/spaces/types.js +2 -0
  89. package/dist/methods/spaces/types.js.map +1 -0
  90. package/dist/methods/tags/tags.d.ts +54 -0
  91. package/dist/methods/tags/tags.d.ts.map +1 -0
  92. package/dist/methods/tags/tags.js +65 -0
  93. package/dist/methods/tags/tags.js.map +1 -0
  94. package/dist/methods/tags/types.d.ts +27 -0
  95. package/dist/methods/tags/types.d.ts.map +1 -0
  96. package/dist/methods/tags/types.js +2 -0
  97. package/dist/methods/tags/types.js.map +1 -0
  98. package/dist/methods/tasks/tasks.d.ts +366 -0
  99. package/dist/methods/tasks/tasks.d.ts.map +1 -0
  100. package/dist/methods/tasks/tasks.js +214 -0
  101. package/dist/methods/tasks/tasks.js.map +1 -0
  102. package/dist/methods/tasks/types.d.ts +163 -0
  103. package/dist/methods/tasks/types.d.ts.map +1 -0
  104. package/dist/methods/tasks/types.js +2 -0
  105. package/dist/methods/tasks/types.js.map +1 -0
  106. package/dist/types/clickup.types.d.ts +35 -0
  107. package/dist/types/clickup.types.d.ts.map +1 -0
  108. package/dist/types/clickup.types.js +2 -0
  109. package/dist/types/clickup.types.js.map +1 -0
  110. package/dist/types/config.types.d.ts +14 -0
  111. package/dist/types/config.types.d.ts.map +1 -0
  112. package/dist/types/config.types.js +2 -0
  113. package/dist/types/config.types.js.map +1 -0
  114. package/dist/types/index.d.ts +14 -0
  115. package/dist/types/index.d.ts.map +1 -0
  116. package/dist/types/index.js +15 -0
  117. package/dist/types/index.js.map +1 -0
  118. package/licence +21 -0
  119. package/package.json +57 -0
@@ -0,0 +1,27 @@
1
+ import { Base } from "../base";
2
+ export class Attachments extends Base {
3
+ /**
4
+ * Create a new task attachment
5
+ *
6
+ * @param params paramaters for creating a task attachment
7
+ * @returns the file stored in ClickUp or an error
8
+ * @see https://developer.clickup.com/reference/createtaskattachment
9
+ */
10
+ async createTaskAttachement(params) {
11
+ // Clicup expects a multipart/form-data request
12
+ // Create new formdata
13
+ const formData = new FormData();
14
+ // Sanitize file name by replacing spaces with dashes
15
+ const sanitizedFileName = params.attachment.name.replace(/\s+/g, "-");
16
+ // Append the attachment to the form data
17
+ formData.append(`attachment`, params.attachment, sanitizedFileName);
18
+ return this.request(`/task/${params.task_id}/attachment`, {
19
+ method: "POST",
20
+ query: {
21
+ custom_task_ids: params.custom_task_ids,
22
+ team_id: params.team_id,
23
+ },
24
+ });
25
+ }
26
+ }
27
+ //# sourceMappingURL=attachments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments.js","sourceRoot":"","sources":["../../../src/methods/attachments/attachments.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAM/B,MAAM,OAAO,WAAY,SAAQ,IAAI;IACnC;;;;;;OAMG;IACI,KAAK,CAAC,qBAAqB,CAAC,MAAkC;QACnE,+CAA+C;QAE/C,sBAAsB;QACtB,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;QAChC,qDAAqD;QACrD,MAAM,iBAAiB,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACtE,yCAAyC;QACzC,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAC;QAEpE,OAAO,IAAI,CAAC,OAAO,CACjB,SAAS,MAAM,CAAC,OAAO,aAAa,EACpC;YACE,MAAM,EAAE,MAAM;YACd,KAAK,EAAE;gBACL,eAAe,EAAE,MAAM,CAAC,eAAe;gBACvC,OAAO,EAAE,MAAM,CAAC,OAAO;aACxB;SACF,CACF,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,28 @@
1
+ export type CreateTaskAttachemntParams = CreateTaskAttachemntParamsBase & CreateTaskAttachemntParamsConditional;
2
+ type CreateTaskAttachemntParamsBase = {
3
+ task_id: string;
4
+ /**
5
+ * The attachment to upload.
6
+ * @description this differs from the documentation. Yet attachment `array` is not the correct type.
7
+ */
8
+ attachment: File;
9
+ };
10
+ type CreateTaskAttachemntParamsConditional = {
11
+ custom_task_ids: true;
12
+ team_id: number;
13
+ } | {
14
+ custom_task_ids?: false;
15
+ team_id?: number;
16
+ };
17
+ export type CreateTaskAttachemntResponse = {
18
+ id: string;
19
+ version: string;
20
+ date: number;
21
+ title: string;
22
+ extension: string;
23
+ thumbnail_small: string;
24
+ thumbnail_large: string;
25
+ url: string;
26
+ };
27
+ export {};
28
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/methods/attachments/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,0BAA0B,GAAG,8BAA8B,GACrE,qCAAqC,CAAC;AAExC,KAAK,8BAA8B,GAAG;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,UAAU,EAAE,IAAI,CAAC;CAClB,CAAC;AAEF,KAAK,qCAAqC,GACtC;IACE,eAAe,EAAE,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB,GACD;IACE,eAAe,CAAC,EAAE,KAAK,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEN,MAAM,MAAM,4BAA4B,GAAG;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/methods/attachments/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,18 @@
1
+ import { Base } from "../base";
2
+ import type { AccessTokenResponse, GetAccessTokenParams, GetAuthorizedUserResponse } from "./types";
3
+ export declare class Authorization extends Base {
4
+ /**
5
+ *
6
+ * @param params the parameters to get an access token
7
+ * @returns data or an error
8
+ * @see https://developer.clickup.com/reference/getaccesstoken
9
+ */
10
+ getAccessToken(params: GetAccessTokenParams): Promise<import("../../interfaces").Response<AccessTokenResponse>>;
11
+ /**
12
+ * Get the authorized user connected to the current token
13
+ * @returns data or an error
14
+ * @see https://developer.clickup.com/reference/getauthorizeduser
15
+ */
16
+ getAuthorizedUser(): Promise<import("../../interfaces").Response<GetAuthorizedUserResponse>>;
17
+ }
18
+ //# sourceMappingURL=authorization.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authorization.d.ts","sourceRoot":"","sources":["../../../src/methods/authorization/authorization.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC/B,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EACpB,yBAAyB,EAC1B,MAAM,SAAS,CAAC;AAEjB,qBAAa,aAAc,SAAQ,IAAI;IACrC;;;;;OAKG;IACU,cAAc,CAAC,MAAM,EAAE,oBAAoB;IASxD;;;;OAIG;IACU,iBAAiB;CAK/B"}
@@ -0,0 +1,28 @@
1
+ import { Base } from "../base";
2
+ export class Authorization extends Base {
3
+ /**
4
+ *
5
+ * @param params the parameters to get an access token
6
+ * @returns data or an error
7
+ * @see https://developer.clickup.com/reference/getaccesstoken
8
+ */
9
+ async getAccessToken(params) {
10
+ return this.request("/oauth/token", {
11
+ method: "POST",
12
+ body: {
13
+ params,
14
+ },
15
+ });
16
+ }
17
+ /**
18
+ * Get the authorized user connected to the current token
19
+ * @returns data or an error
20
+ * @see https://developer.clickup.com/reference/getauthorizeduser
21
+ */
22
+ async getAuthorizedUser() {
23
+ return this.request("/user", {
24
+ method: "GET",
25
+ });
26
+ }
27
+ }
28
+ //# sourceMappingURL=authorization.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authorization.js","sourceRoot":"","sources":["../../../src/methods/authorization/authorization.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAO/B,MAAM,OAAO,aAAc,SAAQ,IAAI;IACrC;;;;;OAKG;IACI,KAAK,CAAC,cAAc,CAAC,MAA4B;QACtD,OAAO,IAAI,CAAC,OAAO,CAAsB,cAAc,EAAE;YACvD,MAAM,EAAE,MAAM;YACd,IAAI,EAAE;gBACJ,MAAM;aACP;SACF,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,iBAAiB;QAC5B,OAAO,IAAI,CAAC,OAAO,CAA4B,OAAO,EAAE;YACtD,MAAM,EAAE,KAAK;SACd,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,13 @@
1
+ import type { User } from "../../types/clickup.types";
2
+ export type GetAccessTokenParams = {
3
+ client_id: string;
4
+ client_secret: string;
5
+ code: string;
6
+ };
7
+ export type AccessTokenResponse = {
8
+ access_token: string;
9
+ };
10
+ export type GetAuthorizedUserResponse = {
11
+ user: User;
12
+ };
13
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/methods/authorization/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAC;AAEtD,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,IAAI,CAAC;CACZ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/methods/authorization/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,161 @@
1
+ import type { ClickUpConfig } from "../types/config.types";
2
+ import type { Response } from "../interfaces";
3
+ type RequestOptions = {
4
+ method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
5
+ body?: Record<string, unknown>;
6
+ query?: Record<string, unknown>;
7
+ headers?: Record<string, string>;
8
+ };
9
+ export declare abstract class Base {
10
+ protected config: ClickUpConfig;
11
+ constructor(config: ClickUpConfig);
12
+ /**
13
+ * Constructs a full URL for the ClickUp API by combining the base URL,
14
+ * endpoint path, and optional query parameters.
15
+ *
16
+ * @param endpoint - The API endpoint path (with or without leading slash)
17
+ * @param query - Optional query parameters to append to the URL
18
+ * @returns The complete URL string
19
+ */
20
+ private buildUrl;
21
+ /**
22
+ * Builds request headers by merging authorization, content-type, config headers,
23
+ * and request-specific headers with proper priority.
24
+ *
25
+ * @param hasBody - Whether the request includes a body (for Content-Type header)
26
+ * @param requestHeaders - Optional request-specific headers to merge
27
+ * @returns Record of header key-value pairs with all values as strings
28
+ */
29
+ private buildHeaders;
30
+ /**
31
+ * Prepares the request body for HTTP methods that support it.
32
+ * Serializes the body to JSON for POST, PUT, and PATCH requests.
33
+ * Returns undefined for GET and DELETE requests, ignoring any provided body.
34
+ *
35
+ * @param method - The HTTP method for the request
36
+ * @param body - Optional body data to serialize
37
+ * @returns JSON string for methods that accept body, undefined otherwise
38
+ */
39
+ private prepareRequestBody;
40
+ /**
41
+ * Extracts error message from HTTP response.
42
+ * @private
43
+ */
44
+ private extractErrorMessage;
45
+ /**
46
+ * Handles HTTP error responses (non-2xx status codes).
47
+ * @private
48
+ */
49
+ private handleHttpError;
50
+ /**
51
+ * Parses successful HTTP response body.
52
+ * @private
53
+ */
54
+ private parseSuccessResponse;
55
+ /**
56
+ * Makes an authenticated HTTP request to the ClickUp API.
57
+ *
58
+ * This protected method provides a reusable interface for all API method
59
+ * implementations, handling authentication, URL construction, header
60
+ * management, and error handling automatically.
61
+ *
62
+ * **Features:**
63
+ * - Automatic authentication with Bearer token from config
64
+ * - Base URL prepending (https://api.clickup.com/api/v2)
65
+ * - URL normalization (handles endpoints with or without leading slash)
66
+ * - Query parameter encoding for GET requests
67
+ * - Request body serialization for POST/PUT/PATCH (ignored for GET/DELETE)
68
+ * - Header merging with proper priority (auth > config > request-specific)
69
+ * - Content-Type header automatically added when body is present
70
+ * - Consistent error handling with structured error responses
71
+ * - JSON response parsing with empty body handling
72
+ *
73
+ * @template T - The expected response data type
74
+ *
75
+ * @param endpoint - The API endpoint path (e.g., "/task" or "task").
76
+ * Leading slash is optional and will be normalized.
77
+ *
78
+ * @param options - Optional request configuration
79
+ * @param options.method - HTTP method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH"
80
+ * (default: "GET")
81
+ * @param options.body - Request body object for POST/PUT/PATCH requests.
82
+ * Automatically serialized to JSON. Ignored for GET/DELETE.
83
+ * @param options.query - Query parameters to append to URL. Values are
84
+ * automatically encoded. Null/undefined values are omitted.
85
+ * @param options.headers - Additional headers to merge with default headers.
86
+ * Cannot override Authorization header.
87
+ *
88
+ * @returns Promise resolving to Response<T> union type:
89
+ * - Success: `{ data: T, error: null }`
90
+ * - Error: `{ data: null, error: ErrorResponse }`
91
+ *
92
+ * **Error Cases:**
93
+ *
94
+ * The method returns structured errors (never throws) for:
95
+ *
96
+ * 1. **Missing API Token** (unauthorized)
97
+ * - Returned when config.apiToken is not set
98
+ * - statusCode: null
99
+ * - message: "API token is required"
100
+ *
101
+ * 2. **401 Unauthorized** (unauthorized)
102
+ * - Returned when API rejects authentication
103
+ * - statusCode: 401
104
+ * - message: Extracted from API response or "Unauthorized"
105
+ *
106
+ * 3. **HTTP Errors** (unknow_error)
107
+ * - Returned for any non-2xx status code (except 401)
108
+ * - statusCode: HTTP status code from response
109
+ * - message: Extracted from API response or status text
110
+ *
111
+ * 4. **Network Errors** (unknow_error)
112
+ * - Returned when fetch fails (connection, timeout, DNS, etc.)
113
+ * - statusCode: null
114
+ * - message: Error message from exception
115
+ *
116
+ * 5. **Invalid JSON** (unknow_error)
117
+ * - Returned when response body is not valid JSON
118
+ * - statusCode: null
119
+ * - message: "Invalid JSON response"
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * // GET request with query parameters
124
+ * const result = await this.request<Task[]>("/task", {
125
+ * query: { archived: false, page: 1 }
126
+ * });
127
+ * if (result.error) {
128
+ * console.error(result.error.message);
129
+ * } else {
130
+ * console.log(result.data); // Task[]
131
+ * }
132
+ *
133
+ * // POST request with body
134
+ * const result = await this.request<Task>("/task", {
135
+ * method: "POST",
136
+ * body: { name: "New Task", description: "Task details" }
137
+ * });
138
+ *
139
+ * // PUT request with custom headers
140
+ * const result = await this.request<Task>("/task/123", {
141
+ * method: "PUT",
142
+ * body: { status: "completed" },
143
+ * headers: { "X-Request-ID": "abc123" }
144
+ * });
145
+ *
146
+ * // DELETE request
147
+ * const result = await this.request<void>("/task/123", {
148
+ * method: "DELETE"
149
+ * });
150
+ *
151
+ * // PATCH request for partial update
152
+ * const result = await this.request<Task>("/task/123", {
153
+ * method: "PATCH",
154
+ * body: { priority: 3 }
155
+ * });
156
+ * ```
157
+ */
158
+ protected request<T>(endpoint: string, options?: RequestOptions): Promise<Response<T>>;
159
+ }
160
+ export {};
161
+ //# sourceMappingURL=base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/methods/base.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,KAAK,cAAc,GAAG;IACpB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;IACrD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,CAAC;AAEF,8BAAsB,IAAI;IACxB,SAAS,CAAC,MAAM,EAAE,aAAa,CAAC;gBAEpB,MAAM,EAAE,aAAa;IAIjC;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ;IAsBhB;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;IAqCpB;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IAiB1B;;;OAGG;YACW,mBAAmB;IAuBjC;;;OAGG;YACW,eAAe;IAgC7B;;;OAGG;YACW,oBAAoB;IA6BlC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAsGG;cACa,OAAO,CAAC,CAAC,EACvB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;CAuDxB"}
@@ -0,0 +1,314 @@
1
+ export class Base {
2
+ constructor(config) {
3
+ this.config = config;
4
+ }
5
+ /**
6
+ * Constructs a full URL for the ClickUp API by combining the base URL,
7
+ * endpoint path, and optional query parameters.
8
+ *
9
+ * @param endpoint - The API endpoint path (with or without leading slash)
10
+ * @param query - Optional query parameters to append to the URL
11
+ * @returns The complete URL string
12
+ */
13
+ buildUrl(endpoint, query) {
14
+ const baseUrl = "https://api.clickup.com/api/v2";
15
+ const normalizedEndpoint = endpoint.startsWith("/")
16
+ ? endpoint
17
+ : `/${endpoint}`;
18
+ const url = `${baseUrl}${normalizedEndpoint}`;
19
+ if (!query || Object.keys(query).length === 0) {
20
+ return url;
21
+ }
22
+ const searchParams = new URLSearchParams();
23
+ for (const [key, value] of Object.entries(query)) {
24
+ if (value !== undefined && value !== null) {
25
+ searchParams.append(key, String(value));
26
+ }
27
+ }
28
+ const queryString = searchParams.toString();
29
+ return queryString ? `${url}?${queryString}` : url;
30
+ }
31
+ /**
32
+ * Builds request headers by merging authorization, content-type, config headers,
33
+ * and request-specific headers with proper priority.
34
+ *
35
+ * @param hasBody - Whether the request includes a body (for Content-Type header)
36
+ * @param requestHeaders - Optional request-specific headers to merge
37
+ * @returns Record of header key-value pairs with all values as strings
38
+ */
39
+ buildHeaders(hasBody, requestHeaders) {
40
+ const headers = {};
41
+ // 1. Start with Authorization header using Bearer token
42
+ headers.Authorization = `Bearer ${this.config.apiToken}`;
43
+ // 2. Add Content-Type when body is present
44
+ if (hasBody) {
45
+ headers["Content-Type"] = "application/json";
46
+ }
47
+ // 3. Merge config.headers if provided
48
+ if (this.config.headers) {
49
+ for (const [key, value] of Object.entries(this.config.headers)) {
50
+ // Don't override Authorization header from config
51
+ if (key !== "Authorization" && value !== undefined && value !== null) {
52
+ headers[key] = String(value);
53
+ }
54
+ }
55
+ }
56
+ // 4. Merge request-specific headers from options
57
+ if (requestHeaders) {
58
+ for (const [key, value] of Object.entries(requestHeaders)) {
59
+ // Don't override Authorization header from request options
60
+ if (key !== "Authorization" && value !== undefined && value !== null) {
61
+ headers[key] = String(value);
62
+ }
63
+ }
64
+ }
65
+ return headers;
66
+ }
67
+ /**
68
+ * Prepares the request body for HTTP methods that support it.
69
+ * Serializes the body to JSON for POST, PUT, and PATCH requests.
70
+ * Returns undefined for GET and DELETE requests, ignoring any provided body.
71
+ *
72
+ * @param method - The HTTP method for the request
73
+ * @param body - Optional body data to serialize
74
+ * @returns JSON string for methods that accept body, undefined otherwise
75
+ */
76
+ prepareRequestBody(method, body) {
77
+ // GET and DELETE requests should not have a body
78
+ if (method === "GET" || method === "DELETE") {
79
+ return undefined;
80
+ }
81
+ // POST, PUT, and PATCH requests serialize body to JSON
82
+ if (body && (method === "POST" || method === "PUT" || method === "PATCH")) {
83
+ return JSON.stringify(body);
84
+ }
85
+ return undefined;
86
+ }
87
+ /**
88
+ * Extracts error message from HTTP response.
89
+ * @private
90
+ */
91
+ async extractErrorMessage(response, defaultMessage) {
92
+ try {
93
+ const errorData = await response.json();
94
+ if (typeof errorData === "object" &&
95
+ errorData !== null &&
96
+ ("message" in errorData || "err" in errorData)) {
97
+ return (errorData.message ||
98
+ errorData.err ||
99
+ defaultMessage);
100
+ }
101
+ return defaultMessage;
102
+ }
103
+ catch {
104
+ return defaultMessage;
105
+ }
106
+ }
107
+ /**
108
+ * Handles HTTP error responses (non-2xx status codes).
109
+ * @private
110
+ */
111
+ async handleHttpError(response) {
112
+ if (response.status === 401) {
113
+ const errorMessage = await this.extractErrorMessage(response, "Unauthorized");
114
+ return {
115
+ error: {
116
+ message: errorMessage,
117
+ statusCode: 401,
118
+ name: "unauthorized",
119
+ },
120
+ data: null,
121
+ };
122
+ }
123
+ const errorMessage = await this.extractErrorMessage(response, response.statusText || "Request failed");
124
+ return {
125
+ error: {
126
+ message: errorMessage,
127
+ statusCode: response.status,
128
+ name: "unknow_error",
129
+ },
130
+ data: null,
131
+ };
132
+ }
133
+ /**
134
+ * Parses successful HTTP response body.
135
+ * @private
136
+ */
137
+ async parseSuccessResponse(response) {
138
+ try {
139
+ const text = await response.text();
140
+ if (!text || text.trim() === "") {
141
+ return {
142
+ data: {},
143
+ error: null,
144
+ };
145
+ }
146
+ const data = JSON.parse(text);
147
+ return {
148
+ data,
149
+ error: null,
150
+ };
151
+ }
152
+ catch {
153
+ return {
154
+ error: {
155
+ message: "Invalid JSON response",
156
+ statusCode: null,
157
+ name: "unknow_error",
158
+ },
159
+ data: null,
160
+ };
161
+ }
162
+ }
163
+ /**
164
+ * Makes an authenticated HTTP request to the ClickUp API.
165
+ *
166
+ * This protected method provides a reusable interface for all API method
167
+ * implementations, handling authentication, URL construction, header
168
+ * management, and error handling automatically.
169
+ *
170
+ * **Features:**
171
+ * - Automatic authentication with Bearer token from config
172
+ * - Base URL prepending (https://api.clickup.com/api/v2)
173
+ * - URL normalization (handles endpoints with or without leading slash)
174
+ * - Query parameter encoding for GET requests
175
+ * - Request body serialization for POST/PUT/PATCH (ignored for GET/DELETE)
176
+ * - Header merging with proper priority (auth > config > request-specific)
177
+ * - Content-Type header automatically added when body is present
178
+ * - Consistent error handling with structured error responses
179
+ * - JSON response parsing with empty body handling
180
+ *
181
+ * @template T - The expected response data type
182
+ *
183
+ * @param endpoint - The API endpoint path (e.g., "/task" or "task").
184
+ * Leading slash is optional and will be normalized.
185
+ *
186
+ * @param options - Optional request configuration
187
+ * @param options.method - HTTP method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH"
188
+ * (default: "GET")
189
+ * @param options.body - Request body object for POST/PUT/PATCH requests.
190
+ * Automatically serialized to JSON. Ignored for GET/DELETE.
191
+ * @param options.query - Query parameters to append to URL. Values are
192
+ * automatically encoded. Null/undefined values are omitted.
193
+ * @param options.headers - Additional headers to merge with default headers.
194
+ * Cannot override Authorization header.
195
+ *
196
+ * @returns Promise resolving to Response<T> union type:
197
+ * - Success: `{ data: T, error: null }`
198
+ * - Error: `{ data: null, error: ErrorResponse }`
199
+ *
200
+ * **Error Cases:**
201
+ *
202
+ * The method returns structured errors (never throws) for:
203
+ *
204
+ * 1. **Missing API Token** (unauthorized)
205
+ * - Returned when config.apiToken is not set
206
+ * - statusCode: null
207
+ * - message: "API token is required"
208
+ *
209
+ * 2. **401 Unauthorized** (unauthorized)
210
+ * - Returned when API rejects authentication
211
+ * - statusCode: 401
212
+ * - message: Extracted from API response or "Unauthorized"
213
+ *
214
+ * 3. **HTTP Errors** (unknow_error)
215
+ * - Returned for any non-2xx status code (except 401)
216
+ * - statusCode: HTTP status code from response
217
+ * - message: Extracted from API response or status text
218
+ *
219
+ * 4. **Network Errors** (unknow_error)
220
+ * - Returned when fetch fails (connection, timeout, DNS, etc.)
221
+ * - statusCode: null
222
+ * - message: Error message from exception
223
+ *
224
+ * 5. **Invalid JSON** (unknow_error)
225
+ * - Returned when response body is not valid JSON
226
+ * - statusCode: null
227
+ * - message: "Invalid JSON response"
228
+ *
229
+ * @example
230
+ * ```typescript
231
+ * // GET request with query parameters
232
+ * const result = await this.request<Task[]>("/task", {
233
+ * query: { archived: false, page: 1 }
234
+ * });
235
+ * if (result.error) {
236
+ * console.error(result.error.message);
237
+ * } else {
238
+ * console.log(result.data); // Task[]
239
+ * }
240
+ *
241
+ * // POST request with body
242
+ * const result = await this.request<Task>("/task", {
243
+ * method: "POST",
244
+ * body: { name: "New Task", description: "Task details" }
245
+ * });
246
+ *
247
+ * // PUT request with custom headers
248
+ * const result = await this.request<Task>("/task/123", {
249
+ * method: "PUT",
250
+ * body: { status: "completed" },
251
+ * headers: { "X-Request-ID": "abc123" }
252
+ * });
253
+ *
254
+ * // DELETE request
255
+ * const result = await this.request<void>("/task/123", {
256
+ * method: "DELETE"
257
+ * });
258
+ *
259
+ * // PATCH request for partial update
260
+ * const result = await this.request<Task>("/task/123", {
261
+ * method: "PATCH",
262
+ * body: { priority: 3 }
263
+ * });
264
+ * ```
265
+ */
266
+ async request(endpoint, options) {
267
+ // 1. Validate API token is present
268
+ if (!this.config.apiToken) {
269
+ return {
270
+ error: {
271
+ message: "API token is required",
272
+ statusCode: null,
273
+ name: "unauthorized",
274
+ },
275
+ data: null,
276
+ };
277
+ }
278
+ // 2. Extract options with defaults
279
+ const method = options?.method || "GET";
280
+ const { body, query, headers: requestHeaders } = options || {};
281
+ // 3. Build full URL with query parameters
282
+ const url = this.buildUrl(endpoint, query);
283
+ // 4. Prepare request body if applicable
284
+ const requestBody = this.prepareRequestBody(method, body);
285
+ // 5. Build headers with authentication
286
+ const headers = this.buildHeaders(!!requestBody, requestHeaders);
287
+ try {
288
+ // 6. Execute fetch with constructed parameters
289
+ const response = await fetch(url, {
290
+ method,
291
+ headers,
292
+ body: requestBody,
293
+ });
294
+ // 7. Handle HTTP errors or parse success response
295
+ if (!response.ok) {
296
+ return this.handleHttpError(response);
297
+ }
298
+ return this.parseSuccessResponse(response);
299
+ }
300
+ catch (error) {
301
+ // Network errors or other exceptions
302
+ const errorMessage = error instanceof Error ? error.message : "Network request failed";
303
+ return {
304
+ error: {
305
+ message: errorMessage,
306
+ statusCode: null,
307
+ name: "unknow_error",
308
+ },
309
+ data: null,
310
+ };
311
+ }
312
+ }
313
+ }
314
+ //# sourceMappingURL=base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/methods/base.ts"],"names":[],"mappings":"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"}