@lti-tool/core 0.9.0 → 0.10.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 (73) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/ltiTool.d.ts +70 -3
  3. package/dist/ltiTool.d.ts.map +1 -1
  4. package/dist/ltiTool.js +116 -3
  5. package/dist/schemas/client.schema.d.ts +1 -1
  6. package/dist/schemas/client.schema.d.ts.map +1 -1
  7. package/dist/schemas/client.schema.js +1 -1
  8. package/dist/schemas/common.schema.d.ts +1 -1
  9. package/dist/schemas/common.schema.d.ts.map +1 -1
  10. package/dist/schemas/common.schema.js +1 -1
  11. package/dist/schemas/deployment.schema.d.ts +1 -1
  12. package/dist/schemas/deployment.schema.d.ts.map +1 -1
  13. package/dist/schemas/deployment.schema.js +1 -1
  14. package/dist/schemas/lti13/ags/lineItem.schema.d.ts +80 -0
  15. package/dist/schemas/lti13/ags/lineItem.schema.d.ts.map +1 -0
  16. package/dist/schemas/lti13/ags/lineItem.schema.js +49 -0
  17. package/dist/schemas/lti13/ags/result.schema.d.ts +65 -0
  18. package/dist/schemas/lti13/ags/result.schema.d.ts.map +1 -0
  19. package/dist/schemas/lti13/ags/result.schema.js +35 -0
  20. package/dist/schemas/lti13/ags/scoreSubmission.schema.d.ts +25 -4
  21. package/dist/schemas/lti13/ags/scoreSubmission.schema.d.ts.map +1 -1
  22. package/dist/schemas/lti13/ags/scoreSubmission.schema.js +2 -1
  23. package/dist/schemas/lti13/claims/baseJwtClaims.schema.d.ts +1 -1
  24. package/dist/schemas/lti13/claims/baseJwtClaims.schema.d.ts.map +1 -1
  25. package/dist/schemas/lti13/claims/baseJwtClaims.schema.js +1 -1
  26. package/dist/schemas/lti13/claims/contextClaims.schema.d.ts +1 -1
  27. package/dist/schemas/lti13/claims/contextClaims.schema.d.ts.map +1 -1
  28. package/dist/schemas/lti13/claims/contextClaims.schema.js +1 -1
  29. package/dist/schemas/lti13/claims/coreLtiClaims.schema.d.ts +1 -1
  30. package/dist/schemas/lti13/claims/coreLtiClaims.schema.d.ts.map +1 -1
  31. package/dist/schemas/lti13/claims/coreLtiClaims.schema.js +1 -1
  32. package/dist/schemas/lti13/claims/platformClaims.schema.d.ts +1 -1
  33. package/dist/schemas/lti13/claims/platformClaims.schema.d.ts.map +1 -1
  34. package/dist/schemas/lti13/claims/platformClaims.schema.js +1 -1
  35. package/dist/schemas/lti13/claims/privacyClaims.schema.d.ts +1 -1
  36. package/dist/schemas/lti13/claims/privacyClaims.schema.d.ts.map +1 -1
  37. package/dist/schemas/lti13/claims/privacyClaims.schema.js +1 -1
  38. package/dist/schemas/lti13/claims/serviceClaims.schema.d.ts +1 -1
  39. package/dist/schemas/lti13/claims/serviceClaims.schema.d.ts.map +1 -1
  40. package/dist/schemas/lti13/claims/serviceClaims.schema.js +1 -1
  41. package/dist/schemas/lti13/lti13JwtPayload.schema.d.ts +1 -1
  42. package/dist/schemas/lti13/lti13JwtPayload.schema.d.ts.map +1 -1
  43. package/dist/schemas/lti13/lti13JwtPayload.schema.js +1 -1
  44. package/dist/schemas/lti13/lti13Launch.schema.d.ts +1 -1
  45. package/dist/schemas/lti13/lti13Launch.schema.d.ts.map +1 -1
  46. package/dist/schemas/lti13/lti13Launch.schema.js +1 -1
  47. package/dist/schemas/lti13/lti13Login.schema.d.ts +1 -1
  48. package/dist/schemas/lti13/lti13Login.schema.d.ts.map +1 -1
  49. package/dist/schemas/lti13/lti13Login.schema.js +1 -1
  50. package/dist/services/ags.service.d.ts +86 -0
  51. package/dist/services/ags.service.d.ts.map +1 -1
  52. package/dist/services/ags.service.js +184 -8
  53. package/dist/services/token.service.d.ts.map +1 -1
  54. package/dist/services/token.service.js +2 -1
  55. package/package.json +1 -1
  56. package/src/ltiTool.ts +144 -5
  57. package/src/schemas/client.schema.ts +1 -1
  58. package/src/schemas/common.schema.ts +1 -1
  59. package/src/schemas/deployment.schema.ts +1 -1
  60. package/src/schemas/lti13/ags/lineItem.schema.ts +85 -0
  61. package/src/schemas/lti13/ags/result.schema.ts +55 -0
  62. package/src/schemas/lti13/ags/scoreSubmission.schema.ts +3 -1
  63. package/src/schemas/lti13/claims/baseJwtClaims.schema.ts +1 -1
  64. package/src/schemas/lti13/claims/contextClaims.schema.ts +1 -1
  65. package/src/schemas/lti13/claims/coreLtiClaims.schema.ts +1 -1
  66. package/src/schemas/lti13/claims/platformClaims.schema.ts +1 -1
  67. package/src/schemas/lti13/claims/privacyClaims.schema.ts +1 -1
  68. package/src/schemas/lti13/claims/serviceClaims.schema.ts +1 -1
  69. package/src/schemas/lti13/lti13JwtPayload.schema.ts +1 -1
  70. package/src/schemas/lti13/lti13Launch.schema.ts +1 -1
  71. package/src/schemas/lti13/lti13Login.schema.ts +1 -1
  72. package/src/services/ags.service.ts +253 -16
  73. package/src/services/token.service.ts +4 -1
package/src/ltiTool.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { SignJWT, createRemoteJWKSet, decodeJwt, exportJWK, jwtVerify } from 'jose';
1
+ import { createRemoteJWKSet, decodeJwt, exportJWK, jwtVerify, SignJWT } from 'jose';
2
2
  import type { Logger } from 'pino';
3
3
 
4
4
  import type { JWKS } from './interfaces/jwks.js';
@@ -14,7 +14,16 @@ import {
14
14
  SessionIdSchema,
15
15
  VerifyLaunchParamsSchema,
16
16
  } from './schemas/index.js';
17
- import type { ScoreSubmission } from './schemas/lti13/ags/scoreSubmission.schema.js';
17
+ import {
18
+ type CreateLineItem,
19
+ type LineItem,
20
+ type LineItems,
21
+ LineItemSchema,
22
+ LineItemsSchema,
23
+ type UpdateLineItem,
24
+ } from './schemas/lti13/ags/lineItem.schema.js';
25
+ import { type Results, ResultsSchema } from './schemas/lti13/ags/result.schema.js';
26
+ import { type ScoreSubmission } from './schemas/lti13/ags/scoreSubmission.schema.js';
18
27
  import { AGSService } from './services/ags.service.js';
19
28
  import { createSession } from './services/session.service.js';
20
29
  import { TokenService } from './services/token.service.js';
@@ -259,17 +268,147 @@ export class LTITool {
259
268
  *
260
269
  * @param session - Active LTI session containing AGS service endpoints
261
270
  * @param score - Score submission data including grade value and user ID
262
- * @returns Result of the score submission
263
271
  * @throws {Error} When AGS is not available or submission fails
264
272
  */
265
- async submitScore(session: LTISession, score: ScoreSubmission): Promise<Response> {
273
+ async submitScore(session: LTISession, score: ScoreSubmission): Promise<void> {
266
274
  if (!session) {
267
275
  throw new Error('session is required');
268
276
  }
269
277
  if (!score) {
270
278
  throw new Error('score is required');
271
279
  }
272
- return await this.agsService.submitScore(session, score);
280
+
281
+ await this.agsService.submitScore(session, score);
282
+ }
283
+
284
+ /**
285
+ * Retrieves all scores for a specific line item from the platform using Assignment and Grade Services (AGS).
286
+ *
287
+ * @param session - Active LTI session containing AGS service endpoints
288
+ * @returns Array of score submissions for the line item
289
+ * @throws {Error} When AGS is not available or request fails
290
+ *
291
+ * @example
292
+ * ```typescript
293
+ * const scores = await ltiTool.getScores(session);
294
+ * console.log('All scores:', scores.map(s => `${s.userId}: ${s.scoreGiven}`));
295
+ * ```
296
+ */
297
+ async getScores(session: LTISession): Promise<Results> {
298
+ if (!session) {
299
+ throw new Error('session is required');
300
+ }
301
+
302
+ const response = await this.agsService.getScores(session);
303
+ const data = await response.json();
304
+ return ResultsSchema.parse(data);
305
+ }
306
+
307
+ /**
308
+ * Retrieves line items (gradebook columns) from the platform using Assignment and Grade Services (AGS).
309
+ *
310
+ * @param session - Active LTI session containing AGS service endpoints
311
+ * @returns Array of line items from the platform
312
+ * @throws {Error} When AGS is not available or request fails
313
+ */
314
+ async listLineItems(session: LTISession): Promise<LineItems> {
315
+ if (!session) {
316
+ throw new Error('session is required');
317
+ }
318
+
319
+ const response = await this.agsService.listLineItems(session);
320
+ const data = await response.json();
321
+ return LineItemsSchema.parse(data);
322
+ }
323
+
324
+ /**
325
+ * Retrieves a specific line item (gradebook column) from the platform using Assignment and Grade Services (AGS).
326
+ *
327
+ * @param session - Active LTI session containing AGS service endpoints
328
+ * @returns Line item data from the platform
329
+ * @throws {Error} When AGS is not available or request fails
330
+ */
331
+ async getLineItem(session: LTISession): Promise<LineItem> {
332
+ if (!session) {
333
+ throw new Error('session is required');
334
+ }
335
+
336
+ const response = await this.agsService.getLineItem(session);
337
+ const data = await response.json();
338
+ return LineItemSchema.parse(data);
339
+ }
340
+
341
+ /**
342
+ * Creates a new line item (gradebook column) on the platform using Assignment and Grade Services (AGS).
343
+ *
344
+ * @param session - Active LTI session containing AGS service endpoints
345
+ * @param createLineItem - Line item data including label, scoreMaximum, and optional metadata
346
+ * @returns Created line item with platform-generated ID and validated data
347
+ * @throws {Error} When AGS is not available, input validation fails, or creation fails
348
+ *
349
+ * @example
350
+ * ```typescript
351
+ * const newLineItem = await ltiTool.createLineItem(session, {
352
+ * label: 'Quiz 1',
353
+ * scoreMaximum: 100,
354
+ * tag: 'quiz',
355
+ * resourceId: 'quiz-001'
356
+ * });
357
+ * console.log('Created line item:', newLineItem.id);
358
+ * ```
359
+ */
360
+ async createLineItem(
361
+ session: LTISession,
362
+ createLineItem: CreateLineItem,
363
+ ): Promise<LineItem> {
364
+ if (!session) {
365
+ throw new Error('session is required');
366
+ }
367
+ if (!createLineItem) {
368
+ throw new Error('createLineItem is required');
369
+ }
370
+
371
+ const response = await this.agsService.createLineItem(session, createLineItem);
372
+ const data = await response.json();
373
+ return LineItemSchema.parse(data);
374
+ }
375
+
376
+ /**
377
+ * Updates an existing line item (gradebook column) on the platform using Assignment and Grade Services (AGS).
378
+ *
379
+ * @param session - Active LTI session containing AGS service endpoints
380
+ * @param updateLineItem - Updated line item data including all required fields
381
+ * @returns Updated line item with validated data from the platform
382
+ * @throws {Error} When AGS is not available, input validation fails, or update fails
383
+ */
384
+ async updateLineItem(
385
+ session: LTISession,
386
+ updateLineItem: UpdateLineItem,
387
+ ): Promise<LineItem> {
388
+ if (!session) {
389
+ throw new Error('session is required');
390
+ }
391
+ if (!updateLineItem) {
392
+ throw new Error('lineItem is required');
393
+ }
394
+
395
+ const response = await this.agsService.updateLineItem(session, updateLineItem);
396
+ const data = await response.json();
397
+ return LineItemSchema.parse(data);
398
+ }
399
+
400
+ /**
401
+ * Deletes a line item (gradebook column) from the platform using Assignment and Grade Services (AGS).
402
+ *
403
+ * @param session - Active LTI session containing AGS service endpoints
404
+ * @throws {Error} When AGS is not available or deletion fails
405
+ */
406
+ async deleteLineItem(session: LTISession): Promise<void> {
407
+ if (!session) {
408
+ throw new Error('session is required');
409
+ }
410
+
411
+ await this.agsService.deleteLineItem(session);
273
412
  }
274
413
 
275
414
  // Client management
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import * as z from 'zod';
2
2
 
3
3
  import { DeploymentSchema } from './deployment.schema';
4
4
 
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import * as z from 'zod';
2
2
 
3
3
  /**
4
4
  * Common validation schemas used across the LTI tool
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import * as z from 'zod';
2
2
 
3
3
  export const DeploymentSchema = z.object({
4
4
  id: z.uuid().describe('Internal stable UUID for this deployment configuration'),
@@ -0,0 +1,85 @@
1
+ import * as z from 'zod';
2
+
3
+ /**
4
+ * Schema for LTI Assignment and Grade Services (AGS) Line Item.
5
+ * Represents a gradebook column/assignment according to LTI AGS v2.0 specification.
6
+ *
7
+ * @see https://www.imsglobal.org/spec/lti-ags/v2p0/#line-item-service
8
+ */
9
+ export const LineItemSchema = z.object({
10
+ /** Unique identifier for the line item */
11
+ id: z.url(),
12
+
13
+ /** Maximum score possible for this line item */
14
+ scoreMaximum: z.number().min(0),
15
+
16
+ /** Human-readable label for the line item */
17
+ label: z.string(),
18
+
19
+ /** Optional resource identifier that this line item is associated with */
20
+ resourceId: z.string().optional(),
21
+
22
+ /** Optional resource link identifier */
23
+ resourceLinkId: z.string().optional(),
24
+
25
+ /** Optional tag to identify the line item */
26
+ tag: z.string().optional(),
27
+
28
+ /** Optional start date/time for the assignment */
29
+ startDateTime: z.iso.datetime().optional(),
30
+
31
+ /** Optional end date/time for the assignment */
32
+ endDateTime: z.iso.datetime().optional(),
33
+ });
34
+
35
+ /**
36
+ * Schema for creating a new LTI Assignment and Grade Services (AGS) Line Item.
37
+ * Omits the 'id' field since it's generated by the platform upon creation.
38
+ *
39
+ * @see https://www.imsglobal.org/spec/lti-ags/v2p0/#line-item-service
40
+ */
41
+ export const CreateLineItemSchema = LineItemSchema.omit({
42
+ id: true,
43
+ });
44
+
45
+ /**
46
+ * Schema for updating an existing LTI Assignment and Grade Services (AGS) Line Item.
47
+ * Omits 'id' and 'resourceLinkId' fields as they are immutable per LTI AGS specification.
48
+ * Tools MUST NOT change these values during updates.
49
+ *
50
+ * @see https://www.imsglobal.org/spec/lti-ags/v2p0/#line-item-service
51
+ */
52
+ export const UpdateLineItemSchema = LineItemSchema.omit({
53
+ id: true,
54
+ resourceLinkId: true,
55
+ });
56
+
57
+ /**
58
+ * Schema for an array of line items returned from the line items service.
59
+ */
60
+ export const LineItemsSchema = z.array(LineItemSchema);
61
+
62
+ // types
63
+
64
+ /**
65
+ * Type representing a validated line item for LTI AGS.
66
+ * Represents a gradebook column or assignment.
67
+ */
68
+ export type LineItem = z.infer<typeof LineItemSchema>;
69
+
70
+ /**
71
+ * Type representing an array of line items.
72
+ */
73
+ export type LineItems = z.infer<typeof LineItemsSchema>;
74
+
75
+ /**
76
+ * Type representing data required to create a new line item for LTI AGS.
77
+ * Contains all LineItem fields except the platform-generated 'id'.
78
+ */
79
+ export type CreateLineItem = z.infer<typeof CreateLineItemSchema>;
80
+
81
+ /**
82
+ * Type representing data for updating an existing line item for LTI AGS.
83
+ * Contains all LineItem fields except immutable 'id' and 'resourceLinkId'.
84
+ */
85
+ export type UpdateLineItem = z.infer<typeof UpdateLineItemSchema>;
@@ -0,0 +1,55 @@
1
+ import * as z from 'zod';
2
+
3
+ /**
4
+ * Schema for LTI Assignment and Grade Services (AGS) Result.
5
+ * Results contain richer metadata than scores, including user info and timestamps.
6
+ *
7
+ * @see https://www.imsglobal.org/spec/lti-ags/v2p0/#result-service
8
+ */
9
+ export const ResultSchema = z.object({
10
+ /** Unique identifier for the result */
11
+ id: z.string(),
12
+
13
+ /** Score of the result, represented as a URL */
14
+ scoreOf: z.url(),
15
+
16
+ /** URL identifying the Line Item to which this result belongs. */
17
+ userId: z.string(),
18
+
19
+ /** The score given to the user */
20
+ resultScore: z.number().optional(),
21
+
22
+ /** Maximum possible score */
23
+ resultMaximum: z.number().optional(),
24
+
25
+ /** Comment associated with the result */
26
+ comment: z.string().optional(),
27
+
28
+ /** Timestamp when the result was recorded */
29
+ timestamp: z.iso.datetime({ offset: true }).optional(),
30
+
31
+ /** Activity progress status */
32
+ activityProgress: z
33
+ .enum(['Initialized', 'Started', 'InProgress', 'Submitted', 'Completed'])
34
+ .optional(),
35
+
36
+ /** Grading progress status */
37
+ gradingProgress: z
38
+ .enum(['FullyGraded', 'Pending', 'PendingManual', 'Failed', 'NotReady'])
39
+ .optional(),
40
+ });
41
+
42
+ /**
43
+ * Schema for an array of results returned from the results service.
44
+ */
45
+ export const ResultsSchema = z.array(ResultSchema);
46
+
47
+ /**
48
+ * Type representing a validated result for LTI AGS.
49
+ */
50
+ export type Result = z.infer<typeof ResultSchema>;
51
+
52
+ /**
53
+ * Type representing an array of results.
54
+ */
55
+ export type Results = z.infer<typeof ResultsSchema>;
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import * as z from 'zod';
2
2
 
3
3
  /**
4
4
  * Schema for submitting grades via LTI Assignment and Grade Services (AGS).
@@ -47,6 +47,8 @@ export const ScoreSubmissionSchema = z.object({
47
47
  .default('FullyGraded'),
48
48
  });
49
49
 
50
+ export const ScoreSubmissionsSchema = z.array(ScoreSubmissionSchema);
51
+
50
52
  /**
51
53
  * Type representing a validated score submission for LTI AGS.
52
54
  * Contains grade data and metadata to be sent to the platform.
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import * as z from 'zod';
2
2
 
3
3
  export const BaseJwtClaimsSchema = z.object({
4
4
  iss: z.string(),
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import * as z from 'zod';
2
2
 
3
3
  export const ResourceLinkSchema = z
4
4
  .object({
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import * as z from 'zod';
2
2
 
3
3
  export const CoreLtiClaimsSchema = z.object({
4
4
  'https://purl.imsglobal.org/spec/lti/claim/message_type': z.union([
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import * as z from 'zod';
2
2
 
3
3
  export const ToolPlatformSchema = z
4
4
  .object({
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import * as z from 'zod';
2
2
 
3
3
  export const PrivacyClaimsSchema = z.object({
4
4
  given_name: z.string(),
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import * as z from 'zod';
2
2
 
3
3
  export const AgsEndpointSchema = z
4
4
  .object({
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import * as z from 'zod';
2
2
 
3
3
  import { BaseJwtClaimsSchema } from './claims/baseJwtClaims.schema.js';
4
4
  import { ContextSchema, ResourceLinkSchema } from './claims/contextClaims.schema.js';
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import * as z from 'zod';
2
2
 
3
3
  export const LTI13LaunchSchema = z.object({
4
4
  id_token: z.jwt(),
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import * as z from 'zod';
2
2
 
3
3
  export const LTI13LoginSchema = z.object({
4
4
  iss: z.string().min(1),