@lti-tool/core 0.12.2 → 0.13.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 (38) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/ltiTool.d.ts +24 -0
  3. package/dist/ltiTool.d.ts.map +1 -1
  4. package/dist/ltiTool.js +34 -1
  5. package/dist/schemas/index.d.ts +1 -0
  6. package/dist/schemas/index.d.ts.map +1 -1
  7. package/dist/schemas/index.js +1 -0
  8. package/dist/schemas/lti13/ags/lineItem.schema.d.ts +3 -3
  9. package/dist/schemas/lti13/deepLinking/contentItem.schema.d.ts +313 -0
  10. package/dist/schemas/lti13/deepLinking/contentItem.schema.d.ts.map +1 -0
  11. package/dist/schemas/lti13/deepLinking/contentItem.schema.js +163 -0
  12. package/dist/schemas/lti13/dynamicRegistration/ltiMessages.schema.d.ts +3 -3
  13. package/dist/schemas/lti13/dynamicRegistration/registrationResponse.schema.d.ts +1 -1
  14. package/dist/schemas/lti13/dynamicRegistration/toolRegistrationPayload.schema.d.ts +2 -2
  15. package/dist/services/ags.service.d.ts +23 -0
  16. package/dist/services/ags.service.d.ts.map +1 -1
  17. package/dist/services/ags.service.js +23 -0
  18. package/dist/services/deepLinking.service.d.ts +61 -0
  19. package/dist/services/deepLinking.service.d.ts.map +1 -0
  20. package/dist/services/deepLinking.service.js +109 -0
  21. package/dist/services/dynamicRegistration.service.d.ts +7 -0
  22. package/dist/services/dynamicRegistration.service.d.ts.map +1 -1
  23. package/dist/services/dynamicRegistration.service.js +7 -0
  24. package/dist/services/nrps.service.d.ts +15 -1
  25. package/dist/services/nrps.service.d.ts.map +1 -1
  26. package/dist/services/nrps.service.js +15 -1
  27. package/dist/services/token.service.d.ts +5 -2
  28. package/dist/services/token.service.d.ts.map +1 -1
  29. package/dist/services/token.service.js +5 -2
  30. package/package.json +1 -1
  31. package/src/ltiTool.ts +44 -1
  32. package/src/schemas/index.ts +9 -0
  33. package/src/schemas/lti13/deepLinking/contentItem.schema.ts +206 -0
  34. package/src/services/ags.service.ts +23 -0
  35. package/src/services/deepLinking.service.ts +128 -0
  36. package/src/services/dynamicRegistration.service.ts +7 -0
  37. package/src/services/nrps.service.ts +15 -1
  38. package/src/services/token.service.ts +5 -2
@@ -0,0 +1,163 @@
1
+ import * as z from 'zod';
2
+ /**
3
+ * Zod schema for base content item properties shared across all content item types.
4
+ * Contains common metadata fields like title, text, icon, and thumbnail.
5
+ *
6
+ * @property title - Optional human-readable title for the content item
7
+ * @property text - Optional descriptive text for the content item
8
+ * @property icon - Optional icon image with URL and dimensions
9
+ * @property thumbnail - Optional thumbnail image with URL and dimensions
10
+ *
11
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#content-item-types
12
+ */
13
+ const BaseContentItemSchema = z.object({
14
+ title: z.string().optional(),
15
+ text: z.string().optional(),
16
+ icon: z
17
+ .object({
18
+ url: z.url(),
19
+ width: z.number().optional(),
20
+ height: z.number().optional(),
21
+ })
22
+ .optional(),
23
+ thumbnail: z
24
+ .object({
25
+ url: z.url(),
26
+ width: z.number().optional(),
27
+ height: z.number().optional(),
28
+ })
29
+ .optional(),
30
+ });
31
+ /**
32
+ * Zod schema for LTI Resource Link content item.
33
+ * Represents a launchable LTI tool resource that can be embedded in the platform.
34
+ *
35
+ * @property type - Always 'ltiResourceLink' for this content type
36
+ * @property url - Optional launch URL for the resource
37
+ * @property custom - Optional custom parameters passed to the tool on launch
38
+ * @property lineItem - Optional gradebook column configuration for this resource
39
+ * @property available - Optional availability window with start/end dates
40
+ * @property submission - Optional submission window with start/end dates
41
+ *
42
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#lti-resource-link
43
+ */
44
+ export const LtiResourceLinkSchema = BaseContentItemSchema.extend({
45
+ type: z.literal('ltiResourceLink'),
46
+ url: z.url().optional(),
47
+ custom: z.record(z.string(), z.string()).optional(),
48
+ lineItem: z
49
+ .object({
50
+ scoreMaximum: z.number().min(0),
51
+ label: z.string(),
52
+ resourceId: z.string().optional(),
53
+ tag: z.string().optional(),
54
+ })
55
+ .optional(),
56
+ available: z
57
+ .object({
58
+ startDateTime: z.iso.datetime().optional(),
59
+ endDateTime: z.iso.datetime().optional(),
60
+ })
61
+ .optional(),
62
+ submission: z
63
+ .object({
64
+ startDateTime: z.iso.datetime().optional(),
65
+ endDateTime: z.iso.datetime().optional(),
66
+ })
67
+ .optional(),
68
+ });
69
+ /**
70
+ * Zod schema for simple link content item.
71
+ * Represents a standard web link that can be opened in various ways.
72
+ *
73
+ * @property type - Always 'link' for this content type
74
+ * @property url - Target URL for the link
75
+ * @property embed - Optional HTML embed code
76
+ * @property window - Optional window configuration for opening the link
77
+ * @property iframe - Optional iframe configuration for embedding the link
78
+ *
79
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#link
80
+ */
81
+ export const LinkSchema = BaseContentItemSchema.extend({
82
+ type: z.literal('link'),
83
+ url: z.url(),
84
+ embed: z
85
+ .object({
86
+ html: z.string(),
87
+ })
88
+ .optional(),
89
+ window: z
90
+ .object({
91
+ targetName: z.string().optional(),
92
+ windowFeatures: z.string().optional(),
93
+ })
94
+ .optional(),
95
+ iframe: z
96
+ .object({
97
+ width: z.number().optional(),
98
+ height: z.number().optional(),
99
+ src: z.url(),
100
+ })
101
+ .optional(),
102
+ });
103
+ /**
104
+ * Zod schema for HTML fragment content item.
105
+ * Represents raw HTML content to be embedded directly in the platform.
106
+ *
107
+ * @property type - Always 'html' for this content type
108
+ * @property html - HTML content to be embedded
109
+ *
110
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#html-fragment
111
+ */
112
+ export const HtmlSchema = BaseContentItemSchema.extend({
113
+ type: z.literal('html'),
114
+ html: z.string(),
115
+ });
116
+ /**
117
+ * Zod schema for file content item.
118
+ * Represents a downloadable file resource.
119
+ *
120
+ * @property type - Always 'file' for this content type
121
+ * @property url - URL to download the file
122
+ * @property mediaType - MIME type of the file
123
+ * @property expiresAt - Optional expiration timestamp for the file URL
124
+ *
125
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#file
126
+ */
127
+ export const FileSchema = BaseContentItemSchema.extend({
128
+ type: z.literal('file'),
129
+ url: z.url(),
130
+ mediaType: z.string(),
131
+ expiresAt: z.iso.datetime().optional(),
132
+ });
133
+ /**
134
+ * Zod schema for image content item.
135
+ * Represents an image resource with optional dimensions.
136
+ *
137
+ * @property type - Always 'image' for this content type
138
+ * @property url - URL to the image
139
+ * @property width - Optional image width in pixels
140
+ * @property height - Optional image height in pixels
141
+ *
142
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#image
143
+ */
144
+ export const ImageSchema = BaseContentItemSchema.extend({
145
+ type: z.literal('image'),
146
+ url: z.url(),
147
+ width: z.number().optional(),
148
+ height: z.number().optional(),
149
+ });
150
+ /**
151
+ * Zod schema for validating any Deep Linking content item type.
152
+ * Uses discriminated union on the 'type' field to determine the specific content item schema.
153
+ * Supports: ltiResourceLink, link, html, file, and image content types.
154
+ *
155
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#content-item-types
156
+ */
157
+ export const ContentItemSchema = z.discriminatedUnion('type', [
158
+ LtiResourceLinkSchema,
159
+ LinkSchema,
160
+ HtmlSchema,
161
+ FileSchema,
162
+ ImageSchema,
163
+ ]);
@@ -33,8 +33,8 @@ declare const LTIDeepLinkingMessageSchema: z.ZodObject<{
33
33
  supported_types: z.ZodOptional<z.ZodArray<z.ZodEnum<{
34
34
  file: "file";
35
35
  ltiResourceLink: "ltiResourceLink";
36
- html: "html";
37
36
  link: "link";
37
+ html: "html";
38
38
  image: "image";
39
39
  }>>>;
40
40
  supported_media_types: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -60,8 +60,8 @@ export declare const LTIMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
60
60
  supported_types: z.ZodOptional<z.ZodArray<z.ZodEnum<{
61
61
  file: "file";
62
62
  ltiResourceLink: "ltiResourceLink";
63
- html: "html";
64
63
  link: "link";
64
+ html: "html";
65
65
  image: "image";
66
66
  }>>>;
67
67
  supported_media_types: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -86,8 +86,8 @@ export declare const LTIMessagesArraySchema: z.ZodArray<z.ZodDiscriminatedUnion<
86
86
  supported_types: z.ZodOptional<z.ZodArray<z.ZodEnum<{
87
87
  file: "file";
88
88
  ltiResourceLink: "ltiResourceLink";
89
- html: "html";
90
89
  link: "link";
90
+ html: "html";
91
91
  image: "image";
92
92
  }>>>;
93
93
  supported_media_types: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -57,8 +57,8 @@ export declare const RegistrationResponseSchema: z.ZodObject<{
57
57
  supported_types: z.ZodOptional<z.ZodArray<z.ZodEnum<{
58
58
  file: "file";
59
59
  ltiResourceLink: "ltiResourceLink";
60
- html: "html";
61
60
  link: "link";
61
+ html: "html";
62
62
  image: "image";
63
63
  }>>>;
64
64
  supported_media_types: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -28,8 +28,8 @@ declare const LTIToolConfigurationSchema: z.ZodObject<{
28
28
  supported_types: z.ZodOptional<z.ZodArray<z.ZodEnum<{
29
29
  file: "file";
30
30
  ltiResourceLink: "ltiResourceLink";
31
- html: "html";
32
31
  link: "link";
32
+ html: "html";
33
33
  image: "image";
34
34
  }>>>;
35
35
  supported_media_types: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -87,8 +87,8 @@ export declare const ToolRegistrationPayloadSchema: z.ZodObject<{
87
87
  supported_types: z.ZodOptional<z.ZodArray<z.ZodEnum<{
88
88
  file: "file";
89
89
  ltiResourceLink: "ltiResourceLink";
90
- html: "html";
91
90
  link: "link";
91
+ html: "html";
92
92
  image: "image";
93
93
  }>>>;
94
94
  supported_media_types: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -14,6 +14,13 @@ export declare class AGSService {
14
14
  private tokenService;
15
15
  private storage;
16
16
  private logger;
17
+ /**
18
+ * Creates a new AGSService instance.
19
+ *
20
+ * @param tokenService - Token service for obtaining OAuth2 bearer tokens
21
+ * @param storage - Storage adapter for retrieving launch configurations
22
+ * @param logger - Logger instance for debug and error logging
23
+ */
17
24
  constructor(tokenService: TokenService, storage: LTIStorage, logger: BaseLogger);
18
25
  /**
19
26
  * Submits a grade score to the platform using LTI Assignment and Grade Services.
@@ -108,6 +115,16 @@ export declare class AGSService {
108
115
  * @param updateLineItem - Updated line item data including all required fields
109
116
  * @returns Promise resolving to the HTTP response containing the updated line item
110
117
  * @throws {Error} When AGS line item service is not available for the session or update fails
118
+ *
119
+ * @example
120
+ * ```typescript
121
+ * const response = await agsService.updateLineItem(session, {
122
+ * label: 'Quiz 1 (Updated)',
123
+ * scoreMaximum: 100,
124
+ * tag: 'quiz'
125
+ * });
126
+ * const updatedLineItem = await response.json();
127
+ * ```
111
128
  */
112
129
  updateLineItem(session: LTISession, updateLineItem: UpdateLineItem): Promise<Response>;
113
130
  /**
@@ -116,6 +133,12 @@ export declare class AGSService {
116
133
  * @param session - Active LTI session containing AGS line item endpoint configuration
117
134
  * @returns Promise resolving to the HTTP response (typically 204 No Content on success)
118
135
  * @throws {Error} When AGS line item service is not available for the session or deletion fails
136
+ *
137
+ * @example
138
+ * ```typescript
139
+ * const response = await agsService.deleteLineItem(session);
140
+ * console.log('Line item deleted successfully');
141
+ * ```
119
142
  */
120
143
  deleteLineItem(session: LTISession): Promise<Response>;
121
144
  private getAGSToken;
@@ -1 +1 @@
1
- {"version":3,"file":"ags.service.d.ts","sourceRoot":"","sources":["../../src/services/ags.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAEvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACf,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAC;AAGtF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;;;;GAKG;AACH,qBAAa,UAAU;IAEnB,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,MAAM;gBAFN,YAAY,EAAE,YAAY,EAC1B,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,UAAU;IAG5B;;;;;;;;;;;;;;;;;;OAkBG;IACG,WAAW,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC;IAiCjF;;;;;;;;;;;;;OAaG;IACG,SAAS,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC;IA4BvD;;;;;;;;;;;;;OAaG;IACG,aAAa,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC;IAsB3D;;;;;;;;;;;;;OAaG;IACG,WAAW,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC;IAsBzD;;;;;;;;;;;;;;;;;;;OAmBG;IACG,cAAc,CAClB,OAAO,EAAE,UAAU,EACnB,cAAc,EAAE,cAAc,GAC7B,OAAO,CAAC,QAAQ,CAAC;IAuBpB;;;;;;;OAOG;IACG,cAAc,CAClB,OAAO,EAAE,UAAU,EACnB,cAAc,EAAE,cAAc,GAC7B,OAAO,CAAC,QAAQ,CAAC;IAuBpB;;;;;;OAMG;IACG,cAAc,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC;YAqB9C,WAAW;YAeX,mBAAmB;CAalC"}
1
+ {"version":3,"file":"ags.service.d.ts","sourceRoot":"","sources":["../../src/services/ags.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAEvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACf,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAC;AAGtF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;;;;GAKG;AACH,qBAAa,UAAU;IASnB,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,MAAM;IAVhB;;;;;;OAMG;gBAEO,YAAY,EAAE,YAAY,EAC1B,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,UAAU;IAG5B;;;;;;;;;;;;;;;;;;OAkBG;IACG,WAAW,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC;IAiCjF;;;;;;;;;;;;;OAaG;IACG,SAAS,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC;IA4BvD;;;;;;;;;;;;;OAaG;IACG,aAAa,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC;IAsB3D;;;;;;;;;;;;;OAaG;IACG,WAAW,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC;IAsBzD;;;;;;;;;;;;;;;;;;;OAmBG;IACG,cAAc,CAClB,OAAO,EAAE,UAAU,EACnB,cAAc,EAAE,cAAc,GAC7B,OAAO,CAAC,QAAQ,CAAC;IAuBpB;;;;;;;;;;;;;;;;;OAiBG;IACG,cAAc,CAClB,OAAO,EAAE,UAAU,EACnB,cAAc,EAAE,cAAc,GAC7B,OAAO,CAAC,QAAQ,CAAC;IAuBpB;;;;;;;;;;;;OAYG;IACG,cAAc,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC;YAqB9C,WAAW;YAeX,mBAAmB;CAalC"}
@@ -9,6 +9,13 @@ export class AGSService {
9
9
  tokenService;
10
10
  storage;
11
11
  logger;
12
+ /**
13
+ * Creates a new AGSService instance.
14
+ *
15
+ * @param tokenService - Token service for obtaining OAuth2 bearer tokens
16
+ * @param storage - Storage adapter for retrieving launch configurations
17
+ * @param logger - Logger instance for debug and error logging
18
+ */
12
19
  constructor(tokenService, storage, logger) {
13
20
  this.tokenService = tokenService;
14
21
  this.storage = storage;
@@ -193,6 +200,16 @@ export class AGSService {
193
200
  * @param updateLineItem - Updated line item data including all required fields
194
201
  * @returns Promise resolving to the HTTP response containing the updated line item
195
202
  * @throws {Error} When AGS line item service is not available for the session or update fails
203
+ *
204
+ * @example
205
+ * ```typescript
206
+ * const response = await agsService.updateLineItem(session, {
207
+ * label: 'Quiz 1 (Updated)',
208
+ * scoreMaximum: 100,
209
+ * tag: 'quiz'
210
+ * });
211
+ * const updatedLineItem = await response.json();
212
+ * ```
196
213
  */
197
214
  async updateLineItem(session, updateLineItem) {
198
215
  if (!session.services?.ags?.lineitem) {
@@ -216,6 +233,12 @@ export class AGSService {
216
233
  * @param session - Active LTI session containing AGS line item endpoint configuration
217
234
  * @returns Promise resolving to the HTTP response (typically 204 No Content on success)
218
235
  * @throws {Error} When AGS line item service is not available for the session or deletion fails
236
+ *
237
+ * @example
238
+ * ```typescript
239
+ * const response = await agsService.deleteLineItem(session);
240
+ * console.log('Line item deleted successfully');
241
+ * ```
219
242
  */
220
243
  async deleteLineItem(session) {
221
244
  if (!session.services?.ags?.lineitem) {
@@ -0,0 +1,61 @@
1
+ import type { BaseLogger } from 'pino';
2
+ import type { LTISession } from '../interfaces/ltiSession.js';
3
+ import type { DeepLinkingContentItem } from '../schemas/lti13/deepLinking/contentItem.schema.js';
4
+ /**
5
+ * Deep Linking service for LTI 1.3.
6
+ * Generates signed JWT responses containing selected content items to return to the platform.
7
+ *
8
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0
9
+ */
10
+ export declare class DeepLinkingService {
11
+ private keyPair;
12
+ private logger;
13
+ private keyId;
14
+ /**
15
+ * Creates a new DeepLinkingService instance.
16
+ *
17
+ * @param keyPair - RSA key pair for signing client assertion JWTs (must be RS256 compatible)
18
+ * @param logger - Logger instance for debug and error logging
19
+ * @param keyId - Key identifier for JWT header, should match JWKS key ID (defaults to 'main')
20
+ */
21
+ constructor(keyPair: CryptoKeyPair, logger: BaseLogger, keyId?: string);
22
+ /**
23
+ * Creates a Deep Linking response with selected content items.
24
+ * Generates a signed JWT and returns an HTML form that auto-submits to the platform.
25
+ *
26
+ * @param session - Active LTI session containing Deep Linking configuration
27
+ * @param contentItems - Array of content items selected by the user
28
+ * @returns Promise resolving to an HTML string containing auto-submit form
29
+ * @throws {Error} When Deep Linking is not available for the session
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * const html = await deepLinkingService.createResponse(session, [
34
+ * {
35
+ * type: 'ltiResourceLink',
36
+ * title: 'Quiz 1',
37
+ * url: 'https://tool.example.com/quiz/1'
38
+ * }
39
+ * ]);
40
+ * // Returns HTML form that auto-submits to platform
41
+ * ```
42
+ */
43
+ createResponse(session: LTISession, contentItems: DeepLinkingContentItem[]): Promise<string>;
44
+ /**
45
+ * Creates a signed JWT containing the Deep Linking response payload.
46
+ *
47
+ * @param session - Active LTI session with Deep Linking configuration
48
+ * @param contentItems - Array of selected content items
49
+ * @returns Promise resolving to a signed JWT string
50
+ */
51
+ private createDeepLinkingJWT;
52
+ /**
53
+ * Generates an HTML form that auto-submits the Deep Linking response to the platform.
54
+ *
55
+ * @param returnUrl - Platform's Deep Linking return URL
56
+ * @param jwt - Signed JWT containing the response
57
+ * @returns HTML string with auto-submit form
58
+ */
59
+ private generateAutoSubmitForm;
60
+ }
61
+ //# sourceMappingURL=deepLinking.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deepLinking.service.d.ts","sourceRoot":"","sources":["../../src/services/deepLinking.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAEvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oDAAoD,CAAC;AAEjG;;;;;GAKG;AACH,qBAAa,kBAAkB;IAS3B,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,KAAK;IAVf;;;;;;OAMG;gBAEO,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,UAAU,EAClB,KAAK,SAAS;IAGxB;;;;;;;;;;;;;;;;;;;;OAoBG;IACG,cAAc,CAClB,OAAO,EAAE,UAAU,EACnB,YAAY,EAAE,sBAAsB,EAAE,GACrC,OAAO,CAAC,MAAM,CAAC;IAiBlB;;;;;;OAMG;YACW,oBAAoB;IA6BlC;;;;;;OAMG;IACH,OAAO,CAAC,sBAAsB;CAiB/B"}
@@ -0,0 +1,109 @@
1
+ import { SignJWT } from 'jose';
2
+ /**
3
+ * Deep Linking service for LTI 1.3.
4
+ * Generates signed JWT responses containing selected content items to return to the platform.
5
+ *
6
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0
7
+ */
8
+ export class DeepLinkingService {
9
+ keyPair;
10
+ logger;
11
+ keyId;
12
+ /**
13
+ * Creates a new DeepLinkingService instance.
14
+ *
15
+ * @param keyPair - RSA key pair for signing client assertion JWTs (must be RS256 compatible)
16
+ * @param logger - Logger instance for debug and error logging
17
+ * @param keyId - Key identifier for JWT header, should match JWKS key ID (defaults to 'main')
18
+ */
19
+ constructor(keyPair, logger, keyId = 'main') {
20
+ this.keyPair = keyPair;
21
+ this.logger = logger;
22
+ this.keyId = keyId;
23
+ }
24
+ /**
25
+ * Creates a Deep Linking response with selected content items.
26
+ * Generates a signed JWT and returns an HTML form that auto-submits to the platform.
27
+ *
28
+ * @param session - Active LTI session containing Deep Linking configuration
29
+ * @param contentItems - Array of content items selected by the user
30
+ * @returns Promise resolving to an HTML string containing auto-submit form
31
+ * @throws {Error} When Deep Linking is not available for the session
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * const html = await deepLinkingService.createResponse(session, [
36
+ * {
37
+ * type: 'ltiResourceLink',
38
+ * title: 'Quiz 1',
39
+ * url: 'https://tool.example.com/quiz/1'
40
+ * }
41
+ * ]);
42
+ * // Returns HTML form that auto-submits to platform
43
+ * ```
44
+ */
45
+ async createResponse(session, contentItems) {
46
+ if (!session.services?.deepLinking) {
47
+ throw new Error('Deep Linking not available for this session');
48
+ }
49
+ this.logger.debug({
50
+ contentItemCount: contentItems.length,
51
+ returnUrl: session.services.deepLinking.returnUrl,
52
+ }, 'Creating Deep Linking response');
53
+ const jwt = await this.createDeepLinkingJWT(session, contentItems);
54
+ return this.generateAutoSubmitForm(session.services.deepLinking.returnUrl, jwt);
55
+ }
56
+ /**
57
+ * Creates a signed JWT containing the Deep Linking response payload.
58
+ *
59
+ * @param session - Active LTI session with Deep Linking configuration
60
+ * @param contentItems - Array of selected content items
61
+ * @returns Promise resolving to a signed JWT string
62
+ */
63
+ async createDeepLinkingJWT(session, contentItems) {
64
+ const deepLinking = session.services.deepLinking;
65
+ const payload = {
66
+ iss: session.platform.clientId,
67
+ aud: session.platform.issuer,
68
+ exp: Math.floor(Date.now() / 1000) + 600,
69
+ iat: Math.floor(Date.now() / 1000),
70
+ nonce: crypto.randomUUID(),
71
+ 'https://purl.imsglobal.org/spec/lti/claim/message_type': 'LtiDeepLinkingResponse',
72
+ 'https://purl.imsglobal.org/spec/lti/claim/version': '1.3.0',
73
+ 'https://purl.imsglobal.org/spec/lti/claim/deployment_id': session.platform.deploymentId,
74
+ 'https://purl.imsglobal.org/spec/lti-dl/claim/content_items': contentItems,
75
+ 'https://purl.imsglobal.org/spec/lti-dl/claim/data': deepLinking.data,
76
+ };
77
+ return await new SignJWT(payload)
78
+ .setProtectedHeader({
79
+ alg: 'RS256',
80
+ typ: 'JWT',
81
+ kid: this.keyId,
82
+ })
83
+ .sign(this.keyPair.privateKey);
84
+ }
85
+ /**
86
+ * Generates an HTML form that auto-submits the Deep Linking response to the platform.
87
+ *
88
+ * @param returnUrl - Platform's Deep Linking return URL
89
+ * @param jwt - Signed JWT containing the response
90
+ * @returns HTML string with auto-submit form
91
+ */
92
+ generateAutoSubmitForm(returnUrl, jwt) {
93
+ return `
94
+ <!DOCTYPE html>
95
+ <html>
96
+ <head>
97
+ <title>Returning to platform...</title>
98
+ </head>
99
+ <body>
100
+ <form id="deepLinkingForm" method="POST" action="${returnUrl}">
101
+ <input type="hidden" name="JWT" value="${jwt}" />
102
+ </form>
103
+ <script>
104
+ document.getElementById('deepLinkingForm').submit();
105
+ </script>
106
+ </body>
107
+ </html>`;
108
+ }
109
+ }
@@ -53,6 +53,13 @@ export declare class DynamicRegistrationService {
53
53
  private storage;
54
54
  private dynamicRegistrationConfig;
55
55
  private logger;
56
+ /**
57
+ * Creates a new DynamicRegistrationService instance.
58
+ *
59
+ * @param storage - Storage adapter for persisting client and deployment configurations
60
+ * @param dynamicRegistrationConfig - Tool configuration including URLs and service settings
61
+ * @param logger - Logger instance for debug and error logging
62
+ */
56
63
  constructor(storage: LTIStorage, dynamicRegistrationConfig: DynamicRegistrationConfig, logger: BaseLogger);
57
64
  /**
58
65
  * Fetches and validates the OpenID Connect configuration from an LTI platform during dynamic registration.
@@ -1 +1 @@
1
- {"version":3,"file":"dynamicRegistration.service.d.ts","sourceRoot":"","sources":["../../src/services/dynamicRegistration.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAEvC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAC5E,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,gDAAgD,CAAC;AACpG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uEAAuE,CAAC;AAErH,OAAO,EACL,KAAK,mBAAmB,EAEzB,MAAM,oEAAoE,CAAC;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oEAAoE,CAAC;AAS9G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,qBAAa,0BAA0B;IAEnC,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,yBAAyB;IACjC,OAAO,CAAC,MAAM;gBAFN,OAAO,EAAE,UAAU,EACnB,yBAAyB,EAAE,yBAAyB,EACpD,MAAM,EAAE,UAAU;IAG5B;;;;;;;OAOG;IACG,0BAA0B,CAC9B,mBAAmB,EAAE,mBAAmB,GACvC,OAAO,CAAC,mBAAmB,CAAC;IAkC/B;;;;;;;;OAQG;IACG,2BAA2B,CAC/B,mBAAmB,EAAE,mBAAmB,EACxC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC;IAsClB;;;;;;;OAOG;IACG,2BAA2B,CAC/B,uBAAuB,EAAE,uBAAuB,GAC/C,OAAO,CAAC,MAAM,CAAC;IAoDlB;;;;;;OAMG;IACG,yBAAyB,CAC7B,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,6BAA6B,GAAG,SAAS,CAAC;IAQrD;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;IAoBrB;;;;;;OAMG;IACH,OAAO,CAAC,WAAW;IAoBnB;;;;;;OAMG;IACH,OAAO,CAAC,wBAAwB;YAkClB,mCAAmC;IAgBjD,OAAO,CAAC,0BAA0B;CAiDnC"}
1
+ {"version":3,"file":"dynamicRegistration.service.d.ts","sourceRoot":"","sources":["../../src/services/dynamicRegistration.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAEvC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAC5E,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,gDAAgD,CAAC;AACpG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uEAAuE,CAAC;AAErH,OAAO,EACL,KAAK,mBAAmB,EAEzB,MAAM,oEAAoE,CAAC;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oEAAoE,CAAC;AAS9G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,qBAAa,0BAA0B;IASnC,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,yBAAyB;IACjC,OAAO,CAAC,MAAM;IAVhB;;;;;;OAMG;gBAEO,OAAO,EAAE,UAAU,EACnB,yBAAyB,EAAE,yBAAyB,EACpD,MAAM,EAAE,UAAU;IAG5B;;;;;;;OAOG;IACG,0BAA0B,CAC9B,mBAAmB,EAAE,mBAAmB,GACvC,OAAO,CAAC,mBAAmB,CAAC;IAkC/B;;;;;;;;OAQG;IACG,2BAA2B,CAC/B,mBAAmB,EAAE,mBAAmB,EACxC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC;IAsClB;;;;;;;OAOG;IACG,2BAA2B,CAC/B,uBAAuB,EAAE,uBAAuB,GAC/C,OAAO,CAAC,MAAM,CAAC;IAoDlB;;;;;;OAMG;IACG,yBAAyB,CAC7B,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,6BAA6B,GAAG,SAAS,CAAC;IAQrD;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;IAoBrB;;;;;;OAMG;IACH,OAAO,CAAC,WAAW;IAoBnB;;;;;;OAMG;IACH,OAAO,CAAC,wBAAwB;YAkClB,mCAAmC;IAgBjD,OAAO,CAAC,0BAA0B;CAiDnC"}
@@ -48,6 +48,13 @@ export class DynamicRegistrationService {
48
48
  storage;
49
49
  dynamicRegistrationConfig;
50
50
  logger;
51
+ /**
52
+ * Creates a new DynamicRegistrationService instance.
53
+ *
54
+ * @param storage - Storage adapter for persisting client and deployment configurations
55
+ * @param dynamicRegistrationConfig - Tool configuration including URLs and service settings
56
+ * @param logger - Logger instance for debug and error logging
57
+ */
51
58
  constructor(storage, dynamicRegistrationConfig, logger) {
52
59
  this.storage = storage;
53
60
  this.dynamicRegistrationConfig = dynamicRegistrationConfig;
@@ -12,14 +12,28 @@ export declare class NRPSService {
12
12
  private tokenService;
13
13
  private storage;
14
14
  private logger;
15
+ /**
16
+ * Creates a new NRPSService instance.
17
+ *
18
+ * @param tokenService - Token service for obtaining OAuth2 bearer tokens
19
+ * @param storage - Storage adapter for retrieving launch configurations
20
+ * @param logger - Logger instance for debug and error logging
21
+ */
15
22
  constructor(tokenService: TokenService, storage: LTIStorage, logger: BaseLogger);
16
23
  /**
17
24
  * Retrieves all members (users) in the current course/context from the platform.
18
25
  * Returns raw response that should be parsed by the calling service.
19
26
  *
20
27
  * @param session - Active LTI session containing NRPS service endpoints
21
- * @returns Raw HTTP response containing membership data
28
+ * @returns Promise resolving to the HTTP response containing membership data
22
29
  * @throws {Error} When NRPS is not available for this session or request fails
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * const response = await nrpsService.getMembers(session);
34
+ * const data = await response.json();
35
+ * console.log('Course members:', data.members);
36
+ * ```
23
37
  */
24
38
  getMembers(session: LTISession): Promise<Response>;
25
39
  private getNRPSToken;
@@ -1 +1 @@
1
- {"version":3,"file":"nrps.service.d.ts","sourceRoot":"","sources":["../../src/services/nrps.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAEvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAG9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;;;;GAKG;AACH,qBAAa,WAAW;IAEpB,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,MAAM;gBAFN,YAAY,EAAE,YAAY,EAC1B,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,UAAU;IAG5B;;;;;;;OAOG;IACG,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC;YAsB1C,YAAY;YAeZ,oBAAoB;CAanC"}
1
+ {"version":3,"file":"nrps.service.d.ts","sourceRoot":"","sources":["../../src/services/nrps.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAEvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAG9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;;;;GAKG;AACH,qBAAa,WAAW;IASpB,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,MAAM;IAVhB;;;;;;OAMG;gBAEO,YAAY,EAAE,YAAY,EAC1B,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,UAAU;IAG5B;;;;;;;;;;;;;;OAcG;IACG,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC;YAsB1C,YAAY;YAeZ,oBAAoB;CAanC"}
@@ -9,6 +9,13 @@ export class NRPSService {
9
9
  tokenService;
10
10
  storage;
11
11
  logger;
12
+ /**
13
+ * Creates a new NRPSService instance.
14
+ *
15
+ * @param tokenService - Token service for obtaining OAuth2 bearer tokens
16
+ * @param storage - Storage adapter for retrieving launch configurations
17
+ * @param logger - Logger instance for debug and error logging
18
+ */
12
19
  constructor(tokenService, storage, logger) {
13
20
  this.tokenService = tokenService;
14
21
  this.storage = storage;
@@ -19,8 +26,15 @@ export class NRPSService {
19
26
  * Returns raw response that should be parsed by the calling service.
20
27
  *
21
28
  * @param session - Active LTI session containing NRPS service endpoints
22
- * @returns Raw HTTP response containing membership data
29
+ * @returns Promise resolving to the HTTP response containing membership data
23
30
  * @throws {Error} When NRPS is not available for this session or request fails
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * const response = await nrpsService.getMembers(session);
35
+ * const data = await response.json();
36
+ * console.log('Course members:', data.members);
37
+ * ```
24
38
  */
25
39
  async getMembers(session) {
26
40
  if (!session.services?.nrps?.membershipUrl) {
@@ -4,6 +4,8 @@
4
4
  *
5
5
  * Implements RFC 7523 (JWT Profile for OAuth 2.0 Client Authentication and Authorization Grants)
6
6
  * as required by LTI 1.3 security framework.
7
+ *
8
+ * @see https://www.rfc-editor.org/rfc/rfc7523
7
9
  */
8
10
  export declare class TokenService {
9
11
  private keyPair;
@@ -20,7 +22,7 @@ export declare class TokenService {
20
22
  *
21
23
  * @param clientId - OAuth2 client identifier
22
24
  * @param tokenUrl - Platform's token endpoint URL
23
- * @returns Signed JWT client assertion
25
+ * @returns Promise resolving to a signed JWT client assertion string
24
26
  */
25
27
  createClientAssertion(clientId: string, tokenUrl: string): Promise<string>;
26
28
  /**
@@ -29,7 +31,8 @@ export declare class TokenService {
29
31
  * @param clientId - OAuth2 client identifier
30
32
  * @param tokenUrl - Platform's token endpoint URL
31
33
  * @param scope - Requested OAuth2 scope (e.g., AGS score scope)
32
- * @returns Bearer access token for API calls
34
+ * @returns Promise resolving to a bearer access token string for API calls
35
+ * @throws {Error} When the token request fails or response is missing access_token
33
36
  */
34
37
  getBearerToken(clientId: string, tokenUrl: string, scope: string): Promise<string>;
35
38
  }
@@ -1 +1 @@
1
- {"version":3,"file":"token.service.d.ts","sourceRoot":"","sources":["../../src/services/token.service.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,qBAAa,YAAY;IAQrB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,KAAK;IARf;;;;;OAKG;gBAEO,OAAO,EAAE,aAAa,EACtB,KAAK,SAAS;IAGxB;;;;;;OAMG;IACG,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiBhF;;;;;;;OAOG;IACG,cAAc,CAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC;CA6BnB"}
1
+ {"version":3,"file":"token.service.d.ts","sourceRoot":"","sources":["../../src/services/token.service.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,qBAAa,YAAY;IAQrB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,KAAK;IARf;;;;;OAKG;gBAEO,OAAO,EAAE,aAAa,EACtB,KAAK,SAAS;IAGxB;;;;;;OAMG;IACG,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiBhF;;;;;;;;OAQG;IACG,cAAc,CAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC;CA6BnB"}
@@ -5,6 +5,8 @@ import { SignJWT } from 'jose';
5
5
  *
6
6
  * Implements RFC 7523 (JWT Profile for OAuth 2.0 Client Authentication and Authorization Grants)
7
7
  * as required by LTI 1.3 security framework.
8
+ *
9
+ * @see https://www.rfc-editor.org/rfc/rfc7523
8
10
  */
9
11
  export class TokenService {
10
12
  keyPair;
@@ -24,7 +26,7 @@ export class TokenService {
24
26
  *
25
27
  * @param clientId - OAuth2 client identifier
26
28
  * @param tokenUrl - Platform's token endpoint URL
27
- * @returns Signed JWT client assertion
29
+ * @returns Promise resolving to a signed JWT client assertion string
28
30
  */
29
31
  async createClientAssertion(clientId, tokenUrl) {
30
32
  return await new SignJWT({
@@ -48,7 +50,8 @@ export class TokenService {
48
50
  * @param clientId - OAuth2 client identifier
49
51
  * @param tokenUrl - Platform's token endpoint URL
50
52
  * @param scope - Requested OAuth2 scope (e.g., AGS score scope)
51
- * @returns Bearer access token for API calls
53
+ * @returns Promise resolving to a bearer access token string for API calls
54
+ * @throws {Error} When the token request fails or response is missing access_token
52
55
  */
53
56
  async getBearerToken(clientId, tokenUrl, scope) {
54
57
  const assertion = await this.createClientAssertion(clientId, tokenUrl);