@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,94 @@
1
+ import type { OAuthAdapter } from "../../core/auth/oauth-adapter";
2
+ import type { OAuthTokens, AuthorizeOptions, CallbackParams, OAuthError } from "../../core/auth/types";
3
+ /**
4
+ * OAuth hook return type
5
+ */
6
+ export interface UseOAuthResult {
7
+ /** Whether the user is authenticated */
8
+ isAuthenticated: boolean;
9
+ /** Whether authentication state is loading */
10
+ isLoading: boolean;
11
+ /** Current tokens (if authenticated) */
12
+ tokens: OAuthTokens | null;
13
+ /** Last error (if any) */
14
+ error: Error | OAuthError | null;
15
+ /** Start OAuth authorization flow */
16
+ authorize: (options?: AuthorizeOptions) => Promise<void>;
17
+ /** Get authorization URL without redirecting */
18
+ getAuthorizeUrl: (options?: AuthorizeOptions) => Promise<string>;
19
+ /** Handle OAuth callback */
20
+ handleCallback: (params: CallbackParams) => Promise<OAuthTokens>;
21
+ /** Logout and revoke tokens */
22
+ logout: () => Promise<void>;
23
+ /** Manually refresh token */
24
+ refreshToken: () => Promise<OAuthTokens | null>;
25
+ }
26
+ /**
27
+ * React hook for OAuth authentication
28
+ *
29
+ * @param adapter - OAuth adapter instance from CrimsonClient
30
+ * @returns OAuth state and methods
31
+ *
32
+ * @example
33
+ * ```tsx
34
+ * const client = createCrimsonClient({
35
+ * apiUrl: 'https://api.example.com',
36
+ * oauth: {
37
+ * clientId: 'my-app',
38
+ * redirectUri: 'https://myapp.com/callback',
39
+ * },
40
+ * });
41
+ *
42
+ * function App() {
43
+ * const oauth = useOAuth(client.getOAuthAdapter());
44
+ *
45
+ * if (oauth.isLoading) return <div>Loading...</div>;
46
+ *
47
+ * if (!oauth.isAuthenticated) {
48
+ * return <button onClick={() => oauth.authorize()}>Login</button>;
49
+ * }
50
+ *
51
+ * return (
52
+ * <div>
53
+ * <p>Authenticated!</p>
54
+ * <button onClick={() => oauth.logout()}>Logout</button>
55
+ * </div>
56
+ * );
57
+ * }
58
+ * ```
59
+ */
60
+ export declare function useOAuth(adapter: OAuthAdapter | undefined): UseOAuthResult;
61
+ /**
62
+ * Hook for handling OAuth callback page
63
+ *
64
+ * @param adapter - OAuth adapter instance
65
+ * @param params - URL parameters from OAuth callback
66
+ * @returns Callback handling state
67
+ *
68
+ * @example
69
+ * ```tsx
70
+ * function CallbackPage() {
71
+ * const params = new URLSearchParams(window.location.search);
72
+ * const { isProcessing, error, success } = useOAuthCallback(
73
+ * client.getOAuthAdapter(),
74
+ * {
75
+ * code: params.get('code') ?? undefined,
76
+ * state: params.get('state') ?? undefined,
77
+ * error: params.get('error') ?? undefined,
78
+ * errorDescription: params.get('error_description') ?? undefined,
79
+ * }
80
+ * );
81
+ *
82
+ * if (isProcessing) return <div>Processing...</div>;
83
+ * if (error) return <div>Error: {error.message}</div>;
84
+ * if (success) return <Navigate to="/" />;
85
+ *
86
+ * return null;
87
+ * }
88
+ * ```
89
+ */
90
+ export declare function useOAuthCallback(adapter: OAuthAdapter | undefined, params: CallbackParams): {
91
+ isProcessing: boolean;
92
+ error: Error | OAuthError | null;
93
+ success: boolean;
94
+ };
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ "use client";
3
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
+ return new (P || (P = Promise))(function (resolve, reject) {
6
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
10
+ });
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.useOAuth = useOAuth;
14
+ exports.useOAuthCallback = useOAuthCallback;
15
+ const react_1 = require("react");
16
+ const emptyState = {
17
+ isAuthenticated: false,
18
+ isLoading: true,
19
+ tokens: null,
20
+ error: null,
21
+ };
22
+ /**
23
+ * React hook for OAuth authentication
24
+ *
25
+ * @param adapter - OAuth adapter instance from CrimsonClient
26
+ * @returns OAuth state and methods
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * const client = createCrimsonClient({
31
+ * apiUrl: 'https://api.example.com',
32
+ * oauth: {
33
+ * clientId: 'my-app',
34
+ * redirectUri: 'https://myapp.com/callback',
35
+ * },
36
+ * });
37
+ *
38
+ * function App() {
39
+ * const oauth = useOAuth(client.getOAuthAdapter());
40
+ *
41
+ * if (oauth.isLoading) return <div>Loading...</div>;
42
+ *
43
+ * if (!oauth.isAuthenticated) {
44
+ * return <button onClick={() => oauth.authorize()}>Login</button>;
45
+ * }
46
+ *
47
+ * return (
48
+ * <div>
49
+ * <p>Authenticated!</p>
50
+ * <button onClick={() => oauth.logout()}>Logout</button>
51
+ * </div>
52
+ * );
53
+ * }
54
+ * ```
55
+ */
56
+ function useOAuth(adapter) {
57
+ const [state, setState] = (0, react_1.useState)(emptyState);
58
+ // Subscribe to auth state changes
59
+ (0, react_1.useEffect)(() => {
60
+ if (!adapter) {
61
+ setState({
62
+ isAuthenticated: false,
63
+ isLoading: false,
64
+ tokens: null,
65
+ error: new Error("OAuth adapter not provided"),
66
+ });
67
+ return;
68
+ }
69
+ // Initialize adapter
70
+ adapter.initialize().catch((error) => {
71
+ setState((prev) => (Object.assign(Object.assign({}, prev), { isLoading: false, error: error instanceof Error ? error : new Error(String(error)) })));
72
+ });
73
+ // Subscribe to state changes
74
+ const unsubscribe = adapter.subscribe((newState) => {
75
+ setState(newState);
76
+ });
77
+ return () => {
78
+ unsubscribe();
79
+ };
80
+ }, [adapter]);
81
+ // Memoized methods
82
+ const authorize = (0, react_1.useCallback)((options) => __awaiter(this, void 0, void 0, function* () {
83
+ if (!adapter) {
84
+ throw new Error("OAuth adapter not provided");
85
+ }
86
+ yield adapter.authorize(options);
87
+ }), [adapter]);
88
+ const getAuthorizeUrl = (0, react_1.useCallback)((options) => __awaiter(this, void 0, void 0, function* () {
89
+ if (!adapter) {
90
+ throw new Error("OAuth adapter not provided");
91
+ }
92
+ return adapter.getAuthorizeUrl(options);
93
+ }), [adapter]);
94
+ const handleCallback = (0, react_1.useCallback)((params) => __awaiter(this, void 0, void 0, function* () {
95
+ if (!adapter) {
96
+ throw new Error("OAuth adapter not provided");
97
+ }
98
+ return adapter.handleCallback(params);
99
+ }), [adapter]);
100
+ const logout = (0, react_1.useCallback)(() => __awaiter(this, void 0, void 0, function* () {
101
+ if (!adapter) {
102
+ throw new Error("OAuth adapter not provided");
103
+ }
104
+ yield adapter.logout();
105
+ }), [adapter]);
106
+ const refreshToken = (0, react_1.useCallback)(() => __awaiter(this, void 0, void 0, function* () {
107
+ if (!adapter) {
108
+ throw new Error("OAuth adapter not provided");
109
+ }
110
+ return adapter.refreshToken();
111
+ }), [adapter]);
112
+ // Return memoized result
113
+ return (0, react_1.useMemo)(() => ({
114
+ isAuthenticated: state.isAuthenticated,
115
+ isLoading: state.isLoading,
116
+ tokens: state.tokens,
117
+ error: state.error,
118
+ authorize,
119
+ getAuthorizeUrl,
120
+ handleCallback,
121
+ logout,
122
+ refreshToken,
123
+ }), [
124
+ state.isAuthenticated,
125
+ state.isLoading,
126
+ state.tokens,
127
+ state.error,
128
+ authorize,
129
+ getAuthorizeUrl,
130
+ handleCallback,
131
+ logout,
132
+ refreshToken,
133
+ ]);
134
+ }
135
+ /**
136
+ * Hook for handling OAuth callback page
137
+ *
138
+ * @param adapter - OAuth adapter instance
139
+ * @param params - URL parameters from OAuth callback
140
+ * @returns Callback handling state
141
+ *
142
+ * @example
143
+ * ```tsx
144
+ * function CallbackPage() {
145
+ * const params = new URLSearchParams(window.location.search);
146
+ * const { isProcessing, error, success } = useOAuthCallback(
147
+ * client.getOAuthAdapter(),
148
+ * {
149
+ * code: params.get('code') ?? undefined,
150
+ * state: params.get('state') ?? undefined,
151
+ * error: params.get('error') ?? undefined,
152
+ * errorDescription: params.get('error_description') ?? undefined,
153
+ * }
154
+ * );
155
+ *
156
+ * if (isProcessing) return <div>Processing...</div>;
157
+ * if (error) return <div>Error: {error.message}</div>;
158
+ * if (success) return <Navigate to="/" />;
159
+ *
160
+ * return null;
161
+ * }
162
+ * ```
163
+ */
164
+ function useOAuthCallback(adapter, params) {
165
+ const [isProcessing, setIsProcessing] = (0, react_1.useState)(true);
166
+ const [error, setError] = (0, react_1.useState)(null);
167
+ const [success, setSuccess] = (0, react_1.useState)(false);
168
+ (0, react_1.useEffect)(() => {
169
+ if (!adapter) {
170
+ setError(new Error("OAuth adapter not provided"));
171
+ setIsProcessing(false);
172
+ return;
173
+ }
174
+ // Check for error in params
175
+ if (params.error) {
176
+ setError({
177
+ error: params.error,
178
+ errorDescription: params.errorDescription,
179
+ });
180
+ setIsProcessing(false);
181
+ return;
182
+ }
183
+ // Process callback
184
+ if (params.code && params.state) {
185
+ adapter
186
+ .handleCallback(params)
187
+ .then(() => {
188
+ setSuccess(true);
189
+ setIsProcessing(false);
190
+ })
191
+ .catch((err) => {
192
+ setError(err instanceof Error ? err : { error: String(err) });
193
+ setIsProcessing(false);
194
+ });
195
+ }
196
+ else {
197
+ setError({
198
+ error: "invalid_request",
199
+ errorDescription: "Missing code or state parameter",
200
+ });
201
+ setIsProcessing(false);
202
+ }
203
+ }, [
204
+ adapter,
205
+ params.code,
206
+ params.state,
207
+ params.error,
208
+ params.errorDescription,
209
+ ]);
210
+ return { isProcessing, error, success };
211
+ }
@@ -0,0 +1,2 @@
1
+ import type { RoadmapContext } from "../../core/types";
2
+ export declare function useRoadmapContext(userId: string | null, enabled: boolean): import("@tanstack/react-query").UseQueryResult<RoadmapContext | null, Error>;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ "use client";
3
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
+ return new (P || (P = Promise))(function (resolve, reject) {
6
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
10
+ });
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.useRoadmapContext = useRoadmapContext;
14
+ const react_query_1 = require("@tanstack/react-query");
15
+ const provider_1 = require("../provider");
16
+ function useRoadmapContext(userId, enabled) {
17
+ const client = (0, provider_1.useCrimsonClient)();
18
+ return (0, react_query_1.useQuery)({
19
+ queryKey: ["roadmap-context", userId],
20
+ enabled: !!userId && enabled,
21
+ queryFn: () => __awaiter(this, void 0, void 0, function* () {
22
+ if (!userId) {
23
+ throw new Error("Missing userId");
24
+ }
25
+ return client.roadmap.context(userId);
26
+ }),
27
+ staleTime: 60 * 1000,
28
+ });
29
+ }
@@ -0,0 +1,24 @@
1
+ import type { StudentProfile, StudentPrefilledInfo } from "../../core/types";
2
+ export declare const studentProfileKeys: {
3
+ all: readonly ["studentProfile"];
4
+ profiles: () => readonly ["studentProfile", "profile"];
5
+ profile: (studentId: string) => readonly ["studentProfile", "profile", string];
6
+ prefilledInfos: () => readonly ["studentProfile", "prefilledInfo"];
7
+ prefilledInfo: (studentId: string) => readonly ["studentProfile", "prefilledInfo", string];
8
+ };
9
+ /**
10
+ * Hook to fetch student profile by student ID
11
+ *
12
+ * @param studentId - The student's user ID
13
+ * @param enabled - Whether to enable the query (default: true)
14
+ * @returns Query result with student profile data
15
+ */
16
+ export declare function useStudentProfile(studentId: string | null, enabled?: boolean): import("@tanstack/react-query").UseQueryResult<StudentProfile | undefined, Error>;
17
+ /**
18
+ * Hook to fetch student prefilled information (application cycle, grade, school, etc.)
19
+ *
20
+ * @param studentId - The student's user ID
21
+ * @param enabled - Whether to enable the query (default: true)
22
+ * @returns Query result with prefilled info including applicationCycle, currentGrade, etc.
23
+ */
24
+ export declare function useStudentPrefilledInfo(studentId: string | null, enabled?: boolean): import("@tanstack/react-query").UseQueryResult<StudentPrefilledInfo | undefined, Error>;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ "use client";
3
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
+ return new (P || (P = Promise))(function (resolve, reject) {
6
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
10
+ });
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.studentProfileKeys = void 0;
14
+ exports.useStudentProfile = useStudentProfile;
15
+ exports.useStudentPrefilledInfo = useStudentPrefilledInfo;
16
+ const react_query_1 = require("@tanstack/react-query");
17
+ const provider_1 = require("../provider");
18
+ // Query keys
19
+ exports.studentProfileKeys = {
20
+ all: ["studentProfile"],
21
+ profiles: () => [...exports.studentProfileKeys.all, "profile"],
22
+ profile: (studentId) => [...exports.studentProfileKeys.profiles(), studentId],
23
+ prefilledInfos: () => [...exports.studentProfileKeys.all, "prefilledInfo"],
24
+ prefilledInfo: (studentId) => [...exports.studentProfileKeys.prefilledInfos(), studentId],
25
+ };
26
+ /**
27
+ * Hook to fetch student profile by student ID
28
+ *
29
+ * @param studentId - The student's user ID
30
+ * @param enabled - Whether to enable the query (default: true)
31
+ * @returns Query result with student profile data
32
+ */
33
+ function useStudentProfile(studentId, enabled = true) {
34
+ const client = (0, provider_1.useCrimsonClient)();
35
+ return (0, react_query_1.useQuery)({
36
+ queryKey: exports.studentProfileKeys.profile(studentId || ""),
37
+ queryFn: () => __awaiter(this, void 0, void 0, function* () {
38
+ if (!studentId) {
39
+ return undefined;
40
+ }
41
+ return client.studentProfile.getProfile(studentId);
42
+ }),
43
+ enabled: enabled && !!studentId,
44
+ });
45
+ }
46
+ /**
47
+ * Hook to fetch student prefilled information (application cycle, grade, school, etc.)
48
+ *
49
+ * @param studentId - The student's user ID
50
+ * @param enabled - Whether to enable the query (default: true)
51
+ * @returns Query result with prefilled info including applicationCycle, currentGrade, etc.
52
+ */
53
+ function useStudentPrefilledInfo(studentId, enabled = true) {
54
+ const client = (0, provider_1.useCrimsonClient)();
55
+ return (0, react_query_1.useQuery)({
56
+ queryKey: exports.studentProfileKeys.prefilledInfo(studentId || ""),
57
+ queryFn: () => __awaiter(this, void 0, void 0, function* () {
58
+ if (!studentId) {
59
+ return undefined;
60
+ }
61
+ return client.studentProfile.getPrefilledInfo(studentId);
62
+ }),
63
+ enabled: enabled && !!studentId,
64
+ });
65
+ }
@@ -0,0 +1,26 @@
1
+ import type { Task, PaginatedResult } from "../../core/types";
2
+ import type { TaskFilters } from "../../core/tasks";
3
+ export declare const taskKeys: {
4
+ all: readonly ["tasks"];
5
+ lists: () => readonly ["tasks", "list"];
6
+ list: (missionId: string) => readonly ["tasks", "list", string];
7
+ infinite: (roadmapId: string, userId: string, filters?: Omit<TaskFilters, "start" | "limit">) => readonly ["tasks", "infinite", string, string, Omit<TaskFilters, "start" | "limit"> | undefined];
8
+ details: () => readonly ["tasks", "detail"];
9
+ detail: (id: string) => readonly ["tasks", "detail", string];
10
+ };
11
+ export declare function useTasks(missionId: string | null, enabled: boolean): import("@tanstack/react-query").UseQueryResult<Task[], Error>;
12
+ export declare function useAllUserTasks(missionLinkIds: string[], enabled: boolean): import("@tanstack/react-query").UseQueryResult<Task[], Error>;
13
+ export declare function useTasksByRoadmapId(roadmapId: string | null, userId: string | null, enabled: boolean): import("@tanstack/react-query").UseQueryResult<Task[], Error>;
14
+ /**
15
+ * Infinite query hook for paginated task loading (requires roadmapId)
16
+ */
17
+ export declare function useTasksInfinite(roadmapId: string | null, userId: string | null, filters?: Omit<TaskFilters, "start" | "limit">, options?: {
18
+ enabled?: boolean;
19
+ pageSize?: number;
20
+ }): import("@tanstack/react-query").UseInfiniteQueryResult<import("@tanstack/react-query").InfiniteData<PaginatedResult<Task>, unknown>, Error>;
21
+ export declare function useCreateTask(): import("@tanstack/react-query").UseMutationResult<Task, Error, Partial<Task>, unknown>;
22
+ export declare function useUpdateTask(): import("@tanstack/react-query").UseMutationResult<Task, Error, {
23
+ id: string;
24
+ data: Partial<Task>;
25
+ }, unknown>;
26
+ export declare function useDeleteTask(): import("@tanstack/react-query").UseMutationResult<void, Error, string, unknown>;
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ "use client";
3
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
+ return new (P || (P = Promise))(function (resolve, reject) {
6
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
10
+ });
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.taskKeys = void 0;
14
+ exports.useTasks = useTasks;
15
+ exports.useAllUserTasks = useAllUserTasks;
16
+ exports.useTasksByRoadmapId = useTasksByRoadmapId;
17
+ exports.useTasksInfinite = useTasksInfinite;
18
+ exports.useCreateTask = useCreateTask;
19
+ exports.useUpdateTask = useUpdateTask;
20
+ exports.useDeleteTask = useDeleteTask;
21
+ const react_query_1 = require("@tanstack/react-query");
22
+ const provider_1 = require("../provider");
23
+ // Query keys
24
+ exports.taskKeys = {
25
+ all: ["tasks"],
26
+ lists: () => [...exports.taskKeys.all, "list"],
27
+ list: (missionId) => [...exports.taskKeys.lists(), missionId],
28
+ infinite: (roadmapId, userId, filters) => [...exports.taskKeys.all, "infinite", roadmapId, userId, filters],
29
+ details: () => [...exports.taskKeys.all, "detail"],
30
+ detail: (id) => [...exports.taskKeys.details(), id],
31
+ };
32
+ function useTasks(missionId, enabled) {
33
+ const client = (0, provider_1.useCrimsonClient)();
34
+ return (0, react_query_1.useQuery)({
35
+ queryKey: exports.taskKeys.list(missionId || ""),
36
+ queryFn: () => __awaiter(this, void 0, void 0, function* () {
37
+ if (!missionId) {
38
+ throw new Error("Missing missionId");
39
+ }
40
+ // Without pagination params, list() returns Task[]
41
+ return (yield client.tasks.list({ missionId }));
42
+ }),
43
+ enabled: !!missionId && enabled,
44
+ });
45
+ }
46
+ function useAllUserTasks(missionLinkIds, enabled) {
47
+ const client = (0, provider_1.useCrimsonClient)();
48
+ return (0, react_query_1.useQuery)({
49
+ queryKey: [
50
+ ...exports.taskKeys.all,
51
+ "missionLinks",
52
+ missionLinkIds.sort().join(","),
53
+ ],
54
+ queryFn: () => __awaiter(this, void 0, void 0, function* () {
55
+ if (!missionLinkIds || missionLinkIds.length === 0) {
56
+ return [];
57
+ }
58
+ // Without pagination params, list() returns Task[]
59
+ return (yield client.tasks.list({ missionLinkIds }));
60
+ }),
61
+ enabled: enabled && missionLinkIds.length > 0,
62
+ });
63
+ }
64
+ function useTasksByRoadmapId(roadmapId, userId, enabled) {
65
+ const client = (0, provider_1.useCrimsonClient)();
66
+ return (0, react_query_1.useQuery)({
67
+ queryKey: [...exports.taskKeys.all, "roadmap", roadmapId, userId],
68
+ queryFn: () => __awaiter(this, void 0, void 0, function* () {
69
+ if (!roadmapId || !userId) {
70
+ return [];
71
+ }
72
+ // Without pagination params, list() returns Task[]
73
+ return (yield client.tasks.list({ roadmapId, userId }));
74
+ }),
75
+ enabled: !!roadmapId && !!userId && enabled,
76
+ });
77
+ }
78
+ /**
79
+ * Infinite query hook for paginated task loading (requires roadmapId)
80
+ */
81
+ function useTasksInfinite(roadmapId, userId, filters, options) {
82
+ var _a, _b;
83
+ const client = (0, provider_1.useCrimsonClient)();
84
+ const enabled = (_a = options === null || options === void 0 ? void 0 : options.enabled) !== null && _a !== void 0 ? _a : true;
85
+ const pageSize = (_b = options === null || options === void 0 ? void 0 : options.pageSize) !== null && _b !== void 0 ? _b : 20;
86
+ return (0, react_query_1.useInfiniteQuery)({
87
+ queryKey: exports.taskKeys.infinite(roadmapId || "", userId || "", filters),
88
+ queryFn: (_a) => __awaiter(this, [_a], void 0, function* ({ pageParam = 0 }) {
89
+ if (!roadmapId || !userId) {
90
+ throw new Error("Missing roadmapId or userId");
91
+ }
92
+ return client.tasks.listPaginated(roadmapId, userId, filters, {
93
+ start: pageParam,
94
+ limit: pageSize,
95
+ });
96
+ }),
97
+ initialPageParam: 0,
98
+ getNextPageParam: (lastPage) => {
99
+ if (!(lastPage === null || lastPage === void 0 ? void 0 : lastPage.pagination))
100
+ return undefined;
101
+ return lastPage.pagination.hasMore
102
+ ? lastPage.pagination.start + lastPage.pagination.limit
103
+ : undefined;
104
+ },
105
+ enabled: !!roadmapId && !!userId && enabled,
106
+ });
107
+ }
108
+ function useCreateTask() {
109
+ const client = (0, provider_1.useCrimsonClient)();
110
+ const queryClient = (0, react_query_1.useQueryClient)();
111
+ return (0, react_query_1.useMutation)({
112
+ mutationFn: (data) => client.tasks.create(data),
113
+ onSuccess: () => {
114
+ queryClient.invalidateQueries({ queryKey: exports.taskKeys.all });
115
+ },
116
+ });
117
+ }
118
+ function useUpdateTask() {
119
+ const client = (0, provider_1.useCrimsonClient)();
120
+ const queryClient = (0, react_query_1.useQueryClient)();
121
+ return (0, react_query_1.useMutation)({
122
+ mutationFn: ({ id, data }) => client.tasks.update(id, data),
123
+ onSuccess: () => {
124
+ queryClient.invalidateQueries({ queryKey: exports.taskKeys.all });
125
+ },
126
+ });
127
+ }
128
+ function useDeleteTask() {
129
+ const client = (0, provider_1.useCrimsonClient)();
130
+ const queryClient = (0, react_query_1.useQueryClient)();
131
+ return (0, react_query_1.useMutation)({
132
+ mutationFn: (id) => client.tasks.delete(id),
133
+ onSuccess: () => {
134
+ queryClient.invalidateQueries({ queryKey: exports.taskKeys.all });
135
+ },
136
+ });
137
+ }
@@ -0,0 +1,9 @@
1
+ import type { User } from "../../core/types";
2
+ export declare const userKeys: {
3
+ all: readonly ["users"];
4
+ details: () => readonly ["users", "detail"];
5
+ detail: (id: string) => readonly ["users", "detail", string];
6
+ list: (ids: string[]) => readonly ["users", "list", string];
7
+ };
8
+ export declare function useUsers(userIds: string[], enabled?: boolean): import("@tanstack/react-query").UseQueryResult<User[], Error>;
9
+ export declare function useUser(userId: string | null, enabled?: boolean): import("@tanstack/react-query").UseQueryResult<User | undefined, Error>;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ "use client";
3
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
+ return new (P || (P = Promise))(function (resolve, reject) {
6
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
10
+ });
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.userKeys = void 0;
14
+ exports.useUsers = useUsers;
15
+ exports.useUser = useUser;
16
+ const react_query_1 = require("@tanstack/react-query");
17
+ const provider_1 = require("../provider");
18
+ // Query keys
19
+ exports.userKeys = {
20
+ all: ["users"],
21
+ details: () => [...exports.userKeys.all, "detail"],
22
+ detail: (id) => [...exports.userKeys.details(), id],
23
+ list: (ids) => [...exports.userKeys.all, "list", ids.sort().join(",")],
24
+ };
25
+ function useUsers(userIds, enabled = true) {
26
+ const client = (0, provider_1.useCrimsonClient)();
27
+ return (0, react_query_1.useQuery)({
28
+ queryKey: exports.userKeys.list(userIds),
29
+ queryFn: () => __awaiter(this, void 0, void 0, function* () {
30
+ if (!userIds || userIds.length === 0) {
31
+ return [];
32
+ }
33
+ return client.users.getByIds(userIds);
34
+ }),
35
+ enabled: enabled && userIds.length > 0,
36
+ });
37
+ }
38
+ function useUser(userId, enabled = true) {
39
+ const client = (0, provider_1.useCrimsonClient)();
40
+ return (0, react_query_1.useQuery)({
41
+ queryKey: exports.userKeys.detail(userId || ""),
42
+ queryFn: () => __awaiter(this, void 0, void 0, function* () {
43
+ if (!userId) {
44
+ return undefined;
45
+ }
46
+ return client.users.getById(userId);
47
+ }),
48
+ enabled: enabled && !!userId,
49
+ });
50
+ }
@@ -0,0 +1,3 @@
1
+ export { CrimsonProvider, useCrimsonClient } from "./provider";
2
+ export type { CrimsonProviderProps } from "./provider";
3
+ export * from "./hooks";