@crimson-education/sdk 0.2.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 (70) hide show
  1. package/README.md +377 -0
  2. package/dist/core/account.d.ts +14 -0
  3. package/dist/core/account.js +30 -0
  4. package/dist/core/auth/index.d.ts +11 -0
  5. package/dist/core/auth/index.js +25 -0
  6. package/dist/core/auth/oauth-adapter.d.ts +78 -0
  7. package/dist/core/auth/oauth-adapter.js +341 -0
  8. package/dist/core/auth/pkce.d.ts +20 -0
  9. package/dist/core/auth/pkce.js +112 -0
  10. package/dist/core/auth/token-manager.d.ts +68 -0
  11. package/dist/core/auth/token-manager.js +294 -0
  12. package/dist/core/auth/token-storage.d.ts +46 -0
  13. package/dist/core/auth/token-storage.js +155 -0
  14. package/dist/core/auth/types.d.ts +148 -0
  15. package/dist/core/auth/types.js +15 -0
  16. package/dist/core/client.d.ts +84 -0
  17. package/dist/core/client.js +229 -0
  18. package/dist/core/index.d.ts +11 -0
  19. package/dist/core/index.js +47 -0
  20. package/dist/core/missionLibrary.d.ts +68 -0
  21. package/dist/core/missionLibrary.js +143 -0
  22. package/dist/core/missions.d.ts +45 -0
  23. package/dist/core/missions.js +140 -0
  24. package/dist/core/roadmap.d.ts +8 -0
  25. package/dist/core/roadmap.js +18 -0
  26. package/dist/core/studentProfile.d.ts +21 -0
  27. package/dist/core/studentProfile.js +41 -0
  28. package/dist/core/tasks.d.ts +117 -0
  29. package/dist/core/tasks.js +288 -0
  30. package/dist/core/types.d.ts +402 -0
  31. package/dist/core/types.js +2 -0
  32. package/dist/core/users.d.ts +21 -0
  33. package/dist/core/users.js +46 -0
  34. package/dist/iframe/auth-state.d.ts +7 -0
  35. package/dist/iframe/auth-state.js +125 -0
  36. package/dist/iframe/constants.d.ts +8 -0
  37. package/dist/iframe/constants.js +29 -0
  38. package/dist/iframe/index.d.ts +5 -0
  39. package/dist/iframe/index.js +17 -0
  40. package/dist/iframe/listener.d.ts +2 -0
  41. package/dist/iframe/listener.js +57 -0
  42. package/dist/iframe/types.d.ts +18 -0
  43. package/dist/iframe/types.js +2 -0
  44. package/dist/index.d.ts +2 -0
  45. package/dist/index.js +22 -0
  46. package/dist/react/hooks/index.d.ts +10 -0
  47. package/dist/react/hooks/index.js +48 -0
  48. package/dist/react/hooks/useAccount.d.ts +13 -0
  49. package/dist/react/hooks/useAccount.js +39 -0
  50. package/dist/react/hooks/useAuthState.d.ts +2 -0
  51. package/dist/react/hooks/useAuthState.js +18 -0
  52. package/dist/react/hooks/useMissionLibrary.d.ts +31 -0
  53. package/dist/react/hooks/useMissionLibrary.js +183 -0
  54. package/dist/react/hooks/useMissions.d.ts +24 -0
  55. package/dist/react/hooks/useMissions.js +104 -0
  56. package/dist/react/hooks/useOAuth.d.ts +94 -0
  57. package/dist/react/hooks/useOAuth.js +211 -0
  58. package/dist/react/hooks/useRoadmapContext.d.ts +2 -0
  59. package/dist/react/hooks/useRoadmapContext.js +29 -0
  60. package/dist/react/hooks/useStudentProfile.d.ts +24 -0
  61. package/dist/react/hooks/useStudentProfile.js +65 -0
  62. package/dist/react/hooks/useTasks.d.ts +26 -0
  63. package/dist/react/hooks/useTasks.js +137 -0
  64. package/dist/react/hooks/useUsers.d.ts +9 -0
  65. package/dist/react/hooks/useUsers.js +50 -0
  66. package/dist/react/index.d.ts +3 -0
  67. package/dist/react/index.js +21 -0
  68. package/dist/react/provider.d.ts +16 -0
  69. package/dist/react/provider.js +41 -0
  70. package/package.json +61 -0
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.MissionsApi = void 0;
13
+ class MissionsApi {
14
+ constructor(client) {
15
+ this.client = client;
16
+ }
17
+ /**
18
+ * List missions for a user.
19
+ * Without pagination params (start/limit), returns MissionsCategory[] (grouped by category).
20
+ * With pagination params, returns PaginatedResult<Mission> (flat list with pagination metadata).
21
+ */
22
+ list(userId, filters) {
23
+ var _a;
24
+ const params = new URLSearchParams({ userId });
25
+ if ((_a = filters === null || filters === void 0 ? void 0 : filters.status) === null || _a === void 0 ? void 0 : _a.length) {
26
+ params.append("status", filters.status.join(","));
27
+ }
28
+ if (filters === null || filters === void 0 ? void 0 : filters.title) {
29
+ params.append("title", filters.title);
30
+ }
31
+ if (filters === null || filters === void 0 ? void 0 : filters.roadmapId) {
32
+ params.append("roadmapId", filters.roadmapId);
33
+ }
34
+ if (filters === null || filters === void 0 ? void 0 : filters.groupBy) {
35
+ params.append("groupBy", filters.groupBy);
36
+ }
37
+ if (filters === null || filters === void 0 ? void 0 : filters.dueDateStart) {
38
+ params.append("dueDateStart", filters.dueDateStart);
39
+ }
40
+ if (filters === null || filters === void 0 ? void 0 : filters.dueDateEnd) {
41
+ params.append("dueDateEnd", filters.dueDateEnd);
42
+ }
43
+ if ((filters === null || filters === void 0 ? void 0 : filters.start) !== undefined) {
44
+ params.append("start", String(filters.start));
45
+ }
46
+ if ((filters === null || filters === void 0 ? void 0 : filters.limit) !== undefined) {
47
+ params.append("limit", String(filters.limit));
48
+ }
49
+ // When pagination params are provided, return paginated result
50
+ if ((filters === null || filters === void 0 ? void 0 : filters.start) !== undefined || (filters === null || filters === void 0 ? void 0 : filters.limit) !== undefined) {
51
+ return this.client.fetch(`/roadmap/missions?${params.toString()}`);
52
+ }
53
+ // Otherwise return category-grouped result (backward compatible)
54
+ return this.client.fetch(`/roadmap/missions?${params.toString()}`);
55
+ }
56
+ /**
57
+ * List missions with pagination (always returns flat list with pagination metadata)
58
+ * Handles both new paginated response and legacy category-grouped response
59
+ */
60
+ listPaginated(userId, filters, pagination) {
61
+ return __awaiter(this, void 0, void 0, function* () {
62
+ var _a, _b;
63
+ const start = (_a = pagination === null || pagination === void 0 ? void 0 : pagination.start) !== null && _a !== void 0 ? _a : 0;
64
+ const limit = (_b = pagination === null || pagination === void 0 ? void 0 : pagination.limit) !== null && _b !== void 0 ? _b : 20;
65
+ const result = yield this.list(userId, Object.assign(Object.assign({}, filters), { start,
66
+ limit }));
67
+ // Check if result is already paginated format
68
+ if (result &&
69
+ typeof result === "object" &&
70
+ "pagination" in result &&
71
+ "data" in result) {
72
+ return result;
73
+ }
74
+ // Legacy format: MissionsCategory[] - convert to paginated format
75
+ // This happens when backend hasn't been updated yet
76
+ const categories = result;
77
+ const allMissions = categories.flatMap((cat) => cat.missions || []);
78
+ const paginatedMissions = allMissions.slice(start, start + limit);
79
+ return {
80
+ data: paginatedMissions,
81
+ pagination: {
82
+ total: allMissions.length,
83
+ start,
84
+ limit,
85
+ hasMore: start + limit < allMissions.length,
86
+ },
87
+ };
88
+ });
89
+ }
90
+ create(data) {
91
+ return this.client.fetch("/roadmap/missions", {
92
+ method: "POST",
93
+ body: JSON.stringify(data),
94
+ });
95
+ }
96
+ update(linkId, data) {
97
+ return this.client.fetch(`/roadmap/missions/${linkId}`, {
98
+ method: "PUT",
99
+ body: JSON.stringify(data),
100
+ });
101
+ }
102
+ delete(linkId) {
103
+ return this.client.fetch(`/roadmap/missions/${linkId}`, {
104
+ method: "DELETE",
105
+ });
106
+ }
107
+ /**
108
+ * Batch edit missions (add, update, delete in a single request)
109
+ * @param userId - The user ID who owns the roadmap
110
+ * @param roadmapId - The roadmap ID
111
+ * @param missions - Array of mission operations
112
+ */
113
+ batchEdit(userId, roadmapId, missions) {
114
+ return this.client.fetch("/roadmap/missions/batch", {
115
+ method: "POST",
116
+ body: JSON.stringify({ userId, roadmapId, missions }),
117
+ });
118
+ }
119
+ /**
120
+ * Batch delete missions and their action items by link IDs
121
+ * @param linkIds - Array of mission link IDs to delete
122
+ */
123
+ batchDelete(linkIds) {
124
+ return this.client.fetch("/roadmap/missions/batch-delete", {
125
+ method: "POST",
126
+ body: JSON.stringify({ linkIds }),
127
+ });
128
+ }
129
+ /**
130
+ * Batch restore deleted missions and their action items by link IDs
131
+ * @param linkIds - Array of mission link IDs to restore
132
+ */
133
+ batchRestore(linkIds) {
134
+ return this.client.fetch("/roadmap/missions/batch-restore", {
135
+ method: "POST",
136
+ body: JSON.stringify({ linkIds }),
137
+ });
138
+ }
139
+ }
140
+ exports.MissionsApi = MissionsApi;
@@ -0,0 +1,8 @@
1
+ import type { CrimsonClient } from "./client";
2
+ import type { RoadmapContext } from "./types";
3
+ export declare class RoadmapApi {
4
+ private client;
5
+ constructor(client: CrimsonClient);
6
+ context(userId: string): Promise<RoadmapContext>;
7
+ createContext(userId: string): Promise<RoadmapContext>;
8
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RoadmapApi = void 0;
4
+ class RoadmapApi {
5
+ constructor(client) {
6
+ this.client = client;
7
+ }
8
+ context(userId) {
9
+ return this.client.fetch(`/roadmap/context?userId=${userId}`);
10
+ }
11
+ createContext(userId) {
12
+ return this.client.fetch("/roadmap/context", {
13
+ method: "POST",
14
+ body: JSON.stringify({ userId }),
15
+ });
16
+ }
17
+ }
18
+ exports.RoadmapApi = RoadmapApi;
@@ -0,0 +1,21 @@
1
+ import type { CrimsonClient } from "./client";
2
+ import type { StudentPrefilledInfo, StudentProfile } from "./types";
3
+ export declare class StudentProfileApi {
4
+ private client;
5
+ constructor(client: CrimsonClient);
6
+ /**
7
+ * Get student profile by student ID
8
+ *
9
+ * @param studentId - The student's user ID
10
+ * @returns Full student profile data
11
+ */
12
+ getProfile(studentId: string): Promise<StudentProfile>;
13
+ /**
14
+ * Get student prefilled information including application cycle, grade, school, etc.
15
+ * Data is aggregated from multiple sources (Salesforce, Student Center, etc.)
16
+ *
17
+ * @param studentId - The student's user ID
18
+ * @returns Prefilled info with source attribution
19
+ */
20
+ getPrefilledInfo(studentId: string): Promise<StudentPrefilledInfo>;
21
+ }
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.StudentProfileApi = void 0;
13
+ class StudentProfileApi {
14
+ constructor(client) {
15
+ this.client = client;
16
+ }
17
+ /**
18
+ * Get student profile by student ID
19
+ *
20
+ * @param studentId - The student's user ID
21
+ * @returns Full student profile data
22
+ */
23
+ getProfile(studentId) {
24
+ return __awaiter(this, void 0, void 0, function* () {
25
+ return this.client.fetch(`/api/v1/student-profile?studentId=${encodeURIComponent(studentId)}`);
26
+ });
27
+ }
28
+ /**
29
+ * Get student prefilled information including application cycle, grade, school, etc.
30
+ * Data is aggregated from multiple sources (Salesforce, Student Center, etc.)
31
+ *
32
+ * @param studentId - The student's user ID
33
+ * @returns Prefilled info with source attribution
34
+ */
35
+ getPrefilledInfo(studentId) {
36
+ return __awaiter(this, void 0, void 0, function* () {
37
+ return this.client.fetch(`/api/v1/student-profile/get-prefilled-info?studentId=${encodeURIComponent(studentId)}`);
38
+ });
39
+ }
40
+ }
41
+ exports.StudentProfileApi = StudentProfileApi;
@@ -0,0 +1,117 @@
1
+ import type { CrimsonClient } from "./client";
2
+ import type { Task, PaginationParams, PaginatedResult, ActionItemResource, AddResourceInput, UploadUrlResponse, UpdateTaskResourceInput } from "./types";
3
+ export type TaskOrderBy = "priority" | "dueDate" | "missionTitle" | "createdAt" | "description";
4
+ export interface TaskFilters extends PaginationParams {
5
+ status?: string[];
6
+ description?: string;
7
+ creatorId?: string;
8
+ dueDateStart?: string;
9
+ dueDateEnd?: string;
10
+ orderBy?: TaskOrderBy;
11
+ order?: "asc" | "desc";
12
+ }
13
+ export declare class TasksApi {
14
+ private client;
15
+ constructor(client: CrimsonClient);
16
+ /**
17
+ * List tasks/action items.
18
+ * Without pagination params, returns Task[] (full list).
19
+ * With pagination params (via roadmapId query), returns PaginatedResult<Task>.
20
+ */
21
+ list(params: {
22
+ missionId?: string;
23
+ missionLinkIds?: string[];
24
+ roadmapId?: string;
25
+ userId?: string;
26
+ }, filters?: TaskFilters): Promise<PaginatedResult<Task>> | Promise<Task[]>;
27
+ /**
28
+ * List tasks with pagination (requires roadmapId, always returns paginated result)
29
+ */
30
+ listPaginated(roadmapId: string, userId: string, filters?: Omit<TaskFilters, "start" | "limit">, pagination?: PaginationParams): Promise<PaginatedResult<Task>>;
31
+ /**
32
+ * Create a new task/action item
33
+ *
34
+ * Two modes are supported:
35
+ * 1. Mission-associated task: Include roadmapMissionId (or missionId)
36
+ * 2. Standalone task: Include only roadmapId (no missionId)
37
+ * - Standalone tasks are not associated with any mission
38
+ *
39
+ * @example
40
+ * // Create mission-associated task
41
+ * tasks.create({ name: "Task 1", roadmapMissionId: "mission-123" })
42
+ *
43
+ * // Create standalone task
44
+ * tasks.create({ name: "Standalone task", roadmapId: "roadmap-456" })
45
+ */
46
+ create(data: Partial<Task>): Promise<Task>;
47
+ /**
48
+ * Create a standalone task (not associated with any mission)
49
+ *
50
+ * @param roadmapId - The roadmap ID to associate the task with
51
+ * @param data - Task data (name/description, dueDate, etc.)
52
+ */
53
+ createStandalone(roadmapId: string, data: Omit<Partial<Task>, "roadmapMissionId" | "missionId">): Promise<Task>;
54
+ update(id: string, data: Partial<Task>): Promise<Task>;
55
+ delete(id: string): Promise<void>;
56
+ /**
57
+ * Add resources to an action item
58
+ * @param actionItemId - The ID of the action item
59
+ * @param resources - Array of resources to add, or a single resource
60
+ */
61
+ addResources(actionItemId: string, resources: AddResourceInput | AddResourceInput[]): Promise<ActionItemResource[]>;
62
+ /**
63
+ * Update task resources (create, update, delete)
64
+ *
65
+ * @param taskId - The ID of the task
66
+ * @param resources - Array of resources. Include id to update existing, omit id to create new.
67
+ * Resources not in the array will be deleted.
68
+ *
69
+ * @example
70
+ * // Update resources: keep existing with id, add new without id
71
+ * tasks.updateResources("task-123", [
72
+ * { id: "existing-id", title: "Updated", type: "Link", url: "https://..." },
73
+ * { title: "New Resource", type: "Upload", url: "https://..." }
74
+ * ])
75
+ */
76
+ updateResources(taskId: string, resources: UpdateTaskResourceInput[]): Promise<Task>;
77
+ /**
78
+ * Get a presigned URL for uploading a file to S3
79
+ *
80
+ * @param filename - The name of the file
81
+ * @param contentType - The MIME type of the file (e.g., "image/png", "application/pdf")
82
+ * @returns Promise with putUrl (for upload), url (final S3 URL), key, and bucket
83
+ *
84
+ * @example
85
+ * // Get upload URL
86
+ * const { putUrl, url } = await tasks.getUploadUrl("document.pdf", "application/pdf");
87
+ *
88
+ * // Upload file using PUT request
89
+ * await fetch(putUrl, { method: "PUT", body: file, headers: { "Content-Type": "application/pdf" } });
90
+ *
91
+ * // Use the url as the resource URL
92
+ * await tasks.updateResources(taskId, [{ title: "Document", type: "Upload", url }]);
93
+ */
94
+ getUploadUrl(filename: string, contentType: string): Promise<UploadUrlResponse>;
95
+ /**
96
+ * Upload a file and get the S3 URL
97
+ *
98
+ * This is a convenience method that:
99
+ * 1. Gets a presigned upload URL
100
+ * 2. Uploads the file
101
+ * 3. Returns the final S3 URL
102
+ *
103
+ * @param file - The File or Blob to upload
104
+ * @param filename - Optional filename (defaults to file.name if File)
105
+ * @returns Promise with the final S3 URL to use as resource URL
106
+ *
107
+ * @example
108
+ * const url = await tasks.uploadFile(file);
109
+ * await tasks.updateResources(taskId, [
110
+ * { title: file.name, type: "Upload", url, mediaType: file.type }
111
+ * ]);
112
+ */
113
+ uploadFile(file: File | Blob, filename?: string): Promise<{
114
+ url: string;
115
+ key: string;
116
+ }>;
117
+ }
@@ -0,0 +1,288 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.TasksApi = void 0;
13
+ const normalizeTask = (raw) => {
14
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
15
+ if (!raw) {
16
+ console.error("normalizeTask received null/undefined:", raw);
17
+ console.trace("Stack trace:");
18
+ throw new Error("Cannot normalize null or undefined task");
19
+ }
20
+ if (!raw.id) {
21
+ console.error("normalizeTask received object without id:", raw);
22
+ throw new Error("Task object missing required 'id' field");
23
+ }
24
+ const statusUpper = typeof raw.status === "string" ? raw.status.toUpperCase() : undefined;
25
+ const isComplete = raw.isComplete !== undefined
26
+ ? Boolean(raw.isComplete)
27
+ : statusUpper
28
+ ? statusUpper === "DONE"
29
+ : Boolean(raw.finishedAt);
30
+ return {
31
+ id: (_a = raw.id) !== null && _a !== void 0 ? _a : "",
32
+ name: (_c = (_b = raw.name) !== null && _b !== void 0 ? _b : raw.description) !== null && _c !== void 0 ? _c : "",
33
+ description: raw.description,
34
+ content: raw.content,
35
+ status: (_d = raw.status) !== null && _d !== void 0 ? _d : "",
36
+ date: (_f = (_e = raw.date) !== null && _e !== void 0 ? _e : raw.dueDate) !== null && _f !== void 0 ? _f : "",
37
+ roadmapMissionId: (_j = (_h = (_g = raw.roadmapMissionId) !== null && _g !== void 0 ? _g : raw.missionId) !== null && _h !== void 0 ? _h : raw.linkId) !== null && _j !== void 0 ? _j : "",
38
+ userId: (_l = (_k = raw.userId) !== null && _k !== void 0 ? _k : raw.creatorId) !== null && _l !== void 0 ? _l : "",
39
+ creatorId: raw.creatorId,
40
+ isComplete,
41
+ resources: raw.resources,
42
+ mission: raw.mission,
43
+ };
44
+ };
45
+ class TasksApi {
46
+ constructor(client) {
47
+ this.client = client;
48
+ }
49
+ /**
50
+ * List tasks/action items.
51
+ * Without pagination params, returns Task[] (full list).
52
+ * With pagination params (via roadmapId query), returns PaginatedResult<Task>.
53
+ */
54
+ list(params, filters) {
55
+ var _a;
56
+ const searchParams = new URLSearchParams();
57
+ if (params === null || params === void 0 ? void 0 : params.missionId) {
58
+ searchParams.append("missionId", params.missionId);
59
+ }
60
+ if ((params === null || params === void 0 ? void 0 : params.missionLinkIds) && params.missionLinkIds.length > 0) {
61
+ searchParams.append("missionLinkIds", params.missionLinkIds.join(","));
62
+ }
63
+ if (params === null || params === void 0 ? void 0 : params.roadmapId) {
64
+ searchParams.append("roadmapId", params.roadmapId);
65
+ }
66
+ if (params === null || params === void 0 ? void 0 : params.userId) {
67
+ searchParams.append("userId", params.userId);
68
+ }
69
+ // Add filter params
70
+ if ((_a = filters === null || filters === void 0 ? void 0 : filters.status) === null || _a === void 0 ? void 0 : _a.length) {
71
+ searchParams.append("status", filters.status.join(","));
72
+ }
73
+ if (filters === null || filters === void 0 ? void 0 : filters.description) {
74
+ searchParams.append("description", filters.description);
75
+ }
76
+ if (filters === null || filters === void 0 ? void 0 : filters.creatorId) {
77
+ searchParams.append("creatorId", filters.creatorId);
78
+ }
79
+ if (filters === null || filters === void 0 ? void 0 : filters.dueDateStart) {
80
+ searchParams.append("dueDateStart", filters.dueDateStart);
81
+ }
82
+ if (filters === null || filters === void 0 ? void 0 : filters.dueDateEnd) {
83
+ searchParams.append("dueDateEnd", filters.dueDateEnd);
84
+ }
85
+ if (filters === null || filters === void 0 ? void 0 : filters.orderBy) {
86
+ searchParams.append("orderBy", filters.orderBy);
87
+ }
88
+ if (filters === null || filters === void 0 ? void 0 : filters.order) {
89
+ searchParams.append("order", filters.order);
90
+ }
91
+ if ((filters === null || filters === void 0 ? void 0 : filters.start) !== undefined) {
92
+ searchParams.append("start", String(filters.start));
93
+ }
94
+ if ((filters === null || filters === void 0 ? void 0 : filters.limit) !== undefined) {
95
+ searchParams.append("limit", String(filters.limit));
96
+ }
97
+ const query = searchParams.toString();
98
+ const path = query
99
+ ? `/roadmap/action-items?${query}`
100
+ : "/roadmap/action-items";
101
+ // When pagination params are provided with roadmapId, return paginated result
102
+ if ((params === null || params === void 0 ? void 0 : params.roadmapId) &&
103
+ ((filters === null || filters === void 0 ? void 0 : filters.start) !== undefined || (filters === null || filters === void 0 ? void 0 : filters.limit) !== undefined)) {
104
+ return this.client
105
+ .fetch(path)
106
+ .then((result) => {
107
+ var _a, _b;
108
+ // Check if result is paginated format
109
+ if (result &&
110
+ typeof result === "object" &&
111
+ "pagination" in result &&
112
+ "data" in result) {
113
+ return {
114
+ data: (result.data || [])
115
+ .filter((item) => item != null)
116
+ .map(normalizeTask),
117
+ pagination: result.pagination,
118
+ };
119
+ }
120
+ // Legacy format: RawTask[] - convert to paginated format
121
+ const allTasks = result || [];
122
+ const start = (_a = filters === null || filters === void 0 ? void 0 : filters.start) !== null && _a !== void 0 ? _a : 0;
123
+ const limit = (_b = filters === null || filters === void 0 ? void 0 : filters.limit) !== null && _b !== void 0 ? _b : 20;
124
+ const normalizedTasks = allTasks
125
+ .filter((item) => item != null)
126
+ .map(normalizeTask);
127
+ const paginatedTasks = normalizedTasks.slice(start, start + limit);
128
+ return {
129
+ data: paginatedTasks,
130
+ pagination: {
131
+ total: normalizedTasks.length,
132
+ start,
133
+ limit,
134
+ hasMore: start + limit < normalizedTasks.length,
135
+ },
136
+ };
137
+ });
138
+ }
139
+ // Otherwise return flat array (backward compatible)
140
+ return this.client.fetch(path).then((items) => {
141
+ const filtered = (items || []).filter((item) => item != null);
142
+ return filtered.map(normalizeTask);
143
+ });
144
+ }
145
+ /**
146
+ * List tasks with pagination (requires roadmapId, always returns paginated result)
147
+ */
148
+ listPaginated(roadmapId, userId, filters, pagination) {
149
+ var _a, _b;
150
+ return this.list({ roadmapId, userId }, Object.assign(Object.assign({}, filters), { start: (_a = pagination === null || pagination === void 0 ? void 0 : pagination.start) !== null && _a !== void 0 ? _a : 0, limit: (_b = pagination === null || pagination === void 0 ? void 0 : pagination.limit) !== null && _b !== void 0 ? _b : 20 }));
151
+ }
152
+ /**
153
+ * Create a new task/action item
154
+ *
155
+ * Two modes are supported:
156
+ * 1. Mission-associated task: Include roadmapMissionId (or missionId)
157
+ * 2. Standalone task: Include only roadmapId (no missionId)
158
+ * - Standalone tasks are not associated with any mission
159
+ *
160
+ * @example
161
+ * // Create mission-associated task
162
+ * tasks.create({ name: "Task 1", roadmapMissionId: "mission-123" })
163
+ *
164
+ * // Create standalone task
165
+ * tasks.create({ name: "Standalone task", roadmapId: "roadmap-456" })
166
+ */
167
+ create(data) {
168
+ return this.client
169
+ .fetch("/roadmap/action-items", {
170
+ method: "POST",
171
+ body: JSON.stringify(data),
172
+ })
173
+ .then((item) => normalizeTask(item));
174
+ }
175
+ /**
176
+ * Create a standalone task (not associated with any mission)
177
+ *
178
+ * @param roadmapId - The roadmap ID to associate the task with
179
+ * @param data - Task data (name/description, dueDate, etc.)
180
+ */
181
+ createStandalone(roadmapId, data) {
182
+ return this.create(Object.assign(Object.assign({}, data), { roadmapId }));
183
+ }
184
+ update(id, data) {
185
+ return this.client
186
+ .fetch(`/roadmap/action-items/${id}`, {
187
+ method: "PUT",
188
+ body: JSON.stringify(data),
189
+ })
190
+ .then((item) => normalizeTask(item));
191
+ }
192
+ delete(id) {
193
+ return this.client.fetch(`/roadmap/action-items/${id}`, {
194
+ method: "DELETE",
195
+ });
196
+ }
197
+ /**
198
+ * Add resources to an action item
199
+ * @param actionItemId - The ID of the action item
200
+ * @param resources - Array of resources to add, or a single resource
201
+ */
202
+ addResources(actionItemId, resources) {
203
+ const body = Array.isArray(resources) ? resources : [resources];
204
+ return this.client.fetch(`/roadmap/action-items/${actionItemId}/resources`, {
205
+ method: "POST",
206
+ body: JSON.stringify(body),
207
+ });
208
+ }
209
+ /**
210
+ * Update task resources (create, update, delete)
211
+ *
212
+ * @param taskId - The ID of the task
213
+ * @param resources - Array of resources. Include id to update existing, omit id to create new.
214
+ * Resources not in the array will be deleted.
215
+ *
216
+ * @example
217
+ * // Update resources: keep existing with id, add new without id
218
+ * tasks.updateResources("task-123", [
219
+ * { id: "existing-id", title: "Updated", type: "Link", url: "https://..." },
220
+ * { title: "New Resource", type: "Upload", url: "https://..." }
221
+ * ])
222
+ */
223
+ updateResources(taskId, resources) {
224
+ return this.update(taskId, { resources });
225
+ }
226
+ /**
227
+ * Get a presigned URL for uploading a file to S3
228
+ *
229
+ * @param filename - The name of the file
230
+ * @param contentType - The MIME type of the file (e.g., "image/png", "application/pdf")
231
+ * @returns Promise with putUrl (for upload), url (final S3 URL), key, and bucket
232
+ *
233
+ * @example
234
+ * // Get upload URL
235
+ * const { putUrl, url } = await tasks.getUploadUrl("document.pdf", "application/pdf");
236
+ *
237
+ * // Upload file using PUT request
238
+ * await fetch(putUrl, { method: "PUT", body: file, headers: { "Content-Type": "application/pdf" } });
239
+ *
240
+ * // Use the url as the resource URL
241
+ * await tasks.updateResources(taskId, [{ title: "Document", type: "Upload", url }]);
242
+ */
243
+ getUploadUrl(filename, contentType) {
244
+ return this.client.fetch("/roadmap/upload", {
245
+ method: "POST",
246
+ body: JSON.stringify({ filename, contentType }),
247
+ });
248
+ }
249
+ /**
250
+ * Upload a file and get the S3 URL
251
+ *
252
+ * This is a convenience method that:
253
+ * 1. Gets a presigned upload URL
254
+ * 2. Uploads the file
255
+ * 3. Returns the final S3 URL
256
+ *
257
+ * @param file - The File or Blob to upload
258
+ * @param filename - Optional filename (defaults to file.name if File)
259
+ * @returns Promise with the final S3 URL to use as resource URL
260
+ *
261
+ * @example
262
+ * const url = await tasks.uploadFile(file);
263
+ * await tasks.updateResources(taskId, [
264
+ * { title: file.name, type: "Upload", url, mediaType: file.type }
265
+ * ]);
266
+ */
267
+ uploadFile(file, filename) {
268
+ return __awaiter(this, void 0, void 0, function* () {
269
+ const name = filename || (file instanceof File ? file.name : `upload-${Date.now()}`);
270
+ const contentType = file.type || "application/octet-stream";
271
+ // Get presigned URL
272
+ const { putUrl, url, key } = yield this.getUploadUrl(name, contentType);
273
+ // Upload file using PUT request
274
+ const response = yield fetch(putUrl, {
275
+ method: "PUT",
276
+ body: file,
277
+ headers: {
278
+ "Content-Type": contentType,
279
+ },
280
+ });
281
+ if (!response.ok) {
282
+ throw new Error(`Upload failed: ${response.status} ${response.statusText}`);
283
+ }
284
+ return { url, key };
285
+ });
286
+ }
287
+ }
288
+ exports.TasksApi = TasksApi;