@lti-tool/core 0.13.1 → 0.14.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.
- package/CHANGELOG.md +12 -0
- package/dist/ltiTool.d.ts.map +1 -1
- package/dist/ltiTool.js +296 -133
- package/dist/schemas/lti13/dynamicRegistration/ltiMessages.schema.d.ts +15 -9
- package/dist/schemas/lti13/dynamicRegistration/ltiMessages.schema.d.ts.map +1 -1
- package/dist/schemas/lti13/dynamicRegistration/ltiMessages.schema.js +7 -1
- package/dist/schemas/lti13/dynamicRegistration/registrationResponse.schema.d.ts +5 -3
- package/dist/schemas/lti13/dynamicRegistration/registrationResponse.schema.d.ts.map +1 -1
- package/dist/schemas/lti13/dynamicRegistration/toolRegistrationPayload.schema.d.ts +10 -6
- package/dist/schemas/lti13/dynamicRegistration/toolRegistrationPayload.schema.d.ts.map +1 -1
- package/dist/services/dynamicRegistration.service.d.ts +12 -0
- package/dist/services/dynamicRegistration.service.d.ts.map +1 -1
- package/dist/services/dynamicRegistration.service.js +68 -11
- package/dist/services/dynamicRegistrationHandlers/moodle.js +1 -1
- package/dist/utils/errorFormatting.d.ts +18 -0
- package/dist/utils/errorFormatting.d.ts.map +1 -0
- package/dist/utils/errorFormatting.js +22 -0
- package/package.json +1 -1
- package/src/ltiTool.ts +364 -165
- package/src/schemas/lti13/dynamicRegistration/ltiMessages.schema.ts +9 -1
- package/src/services/dynamicRegistration.service.ts +85 -10
- package/src/services/dynamicRegistrationHandlers/moodle.ts +1 -1
- package/src/utils/errorFormatting.ts +22 -0
|
@@ -28,7 +28,15 @@ const LTIDeepLinkingMessageSchema = z.object({
|
|
|
28
28
|
target_link_uri: z.url().optional(),
|
|
29
29
|
label: z.string().optional(),
|
|
30
30
|
placements: z
|
|
31
|
-
.array(
|
|
31
|
+
.array(
|
|
32
|
+
z.enum([
|
|
33
|
+
'editor_button',
|
|
34
|
+
'assignment_selection',
|
|
35
|
+
'link_selection',
|
|
36
|
+
'module_index_menu_modal',
|
|
37
|
+
'module_menu_modal',
|
|
38
|
+
]),
|
|
39
|
+
)
|
|
32
40
|
.optional(),
|
|
33
41
|
supported_types: z
|
|
34
42
|
.array(z.enum(['ltiResourceLink', 'file', 'html', 'link', 'image']))
|
|
@@ -189,9 +189,16 @@ export class DynamicRegistrationService {
|
|
|
189
189
|
throw new Error('Invalid or expired session');
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
// Extract platform family from session
|
|
193
|
+
const platformFamily =
|
|
194
|
+
session.openIdConfiguration[
|
|
195
|
+
'https://purl.imsglobal.org/spec/lti-platform-configuration'
|
|
196
|
+
].product_family_code;
|
|
197
|
+
|
|
192
198
|
// 1. build payload
|
|
193
199
|
const toolRegistrationPayload = this.buildRegistrationPayload(
|
|
194
200
|
dynamicRegistrationForm.services ?? [],
|
|
201
|
+
platformFamily,
|
|
195
202
|
);
|
|
196
203
|
|
|
197
204
|
// 2. Post request to Moodle
|
|
@@ -249,29 +256,88 @@ export class DynamicRegistrationService {
|
|
|
249
256
|
return session;
|
|
250
257
|
}
|
|
251
258
|
|
|
259
|
+
/**
|
|
260
|
+
* Builds Canvas-specific deep linking messages for the 5 common placements.
|
|
261
|
+
* Creates separate messages for editor, module menu, assignments, modules page, and link selection.
|
|
262
|
+
*
|
|
263
|
+
* @param deepLinkingUri - URI where deep linking requests should be sent
|
|
264
|
+
* @param toolName - Display name of the tool
|
|
265
|
+
* @returns Array of Canvas deep linking message configurations
|
|
266
|
+
*/
|
|
267
|
+
private buildCanvasDeepLinkingMessages(
|
|
268
|
+
deepLinkingUri: string,
|
|
269
|
+
toolName: string,
|
|
270
|
+
): LTIMessage[] {
|
|
271
|
+
return [
|
|
272
|
+
{
|
|
273
|
+
type: 'LtiDeepLinkingRequest' as const,
|
|
274
|
+
target_link_uri: deepLinkingUri,
|
|
275
|
+
label: toolName,
|
|
276
|
+
placements: ['editor_button' as const],
|
|
277
|
+
supported_types: ['ltiResourceLink' as const],
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
type: 'LtiDeepLinkingRequest' as const,
|
|
281
|
+
target_link_uri: deepLinkingUri,
|
|
282
|
+
label: toolName,
|
|
283
|
+
placements: ['module_menu_modal' as const],
|
|
284
|
+
supported_types: ['ltiResourceLink' as const],
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
type: 'LtiDeepLinkingRequest' as const,
|
|
288
|
+
target_link_uri: deepLinkingUri,
|
|
289
|
+
label: toolName,
|
|
290
|
+
placements: ['assignment_selection' as const],
|
|
291
|
+
supported_types: ['ltiResourceLink' as const],
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
type: 'LtiDeepLinkingRequest' as const,
|
|
295
|
+
target_link_uri: deepLinkingUri,
|
|
296
|
+
label: toolName,
|
|
297
|
+
placements: ['module_index_menu_modal' as const],
|
|
298
|
+
supported_types: ['ltiResourceLink' as const],
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
type: 'LtiDeepLinkingRequest' as const,
|
|
302
|
+
target_link_uri: deepLinkingUri,
|
|
303
|
+
label: toolName,
|
|
304
|
+
placements: ['link_selection' as const],
|
|
305
|
+
supported_types: ['ltiResourceLink' as const],
|
|
306
|
+
},
|
|
307
|
+
];
|
|
308
|
+
}
|
|
309
|
+
|
|
252
310
|
/**
|
|
253
311
|
* Builds array of LTI message types based on selected services during registration.
|
|
254
312
|
* Always includes ResourceLinkRequest, conditionally adds DeepLinkingRequest.
|
|
255
313
|
*
|
|
256
314
|
* @param selectedServices - Array of service names selected by administrator
|
|
257
315
|
* @param deepLinkingUri - URI where deep linking requests should be sent
|
|
316
|
+
* @param platformFamily - Platform family code (e.g., 'canvas', 'moodle')
|
|
317
|
+
* @param toolName - Display name of the tool
|
|
258
318
|
* @returns Array of LTI message configurations for the registration payload
|
|
259
319
|
*/
|
|
260
320
|
private buildMessages(
|
|
261
321
|
selectedServices: string[],
|
|
262
322
|
deepLinkingUri: string,
|
|
323
|
+
platformFamily: string,
|
|
324
|
+
toolName: string,
|
|
263
325
|
): LTIMessage[] {
|
|
264
|
-
const messages = [];
|
|
326
|
+
const messages: LTIMessage[] = [];
|
|
265
327
|
messages.push({ type: 'LtiResourceLinkRequest' as const });
|
|
266
328
|
|
|
267
329
|
if (selectedServices?.includes('deep_linking')) {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
330
|
+
if (platformFamily.toLowerCase() === 'canvas') {
|
|
331
|
+
messages.push(...this.buildCanvasDeepLinkingMessages(deepLinkingUri, toolName));
|
|
332
|
+
} else {
|
|
333
|
+
messages.push({
|
|
334
|
+
type: 'LtiDeepLinkingRequest' as const,
|
|
335
|
+
target_link_uri: deepLinkingUri,
|
|
336
|
+
label: toolName,
|
|
337
|
+
placements: ['editor_button' as const],
|
|
338
|
+
supported_types: ['ltiResourceLink' as const],
|
|
339
|
+
});
|
|
340
|
+
}
|
|
275
341
|
}
|
|
276
342
|
|
|
277
343
|
return messages;
|
|
@@ -309,9 +375,13 @@ export class DynamicRegistrationService {
|
|
|
309
375
|
* Combines tool configuration, selected services, and OAuth parameters into LTI 1.3 registration format.
|
|
310
376
|
*
|
|
311
377
|
* @param selectedServices - Array of service names selected by administrator
|
|
378
|
+
* @param platformFamily - Platform family code (e.g., 'canvas', 'moodle')
|
|
312
379
|
* @returns Complete registration payload ready for platform submission
|
|
313
380
|
*/
|
|
314
|
-
private buildRegistrationPayload(
|
|
381
|
+
private buildRegistrationPayload(
|
|
382
|
+
selectedServices: string[],
|
|
383
|
+
platformFamily: string,
|
|
384
|
+
): ToolRegistrationPayload {
|
|
315
385
|
const config = this.dynamicRegistrationConfig;
|
|
316
386
|
|
|
317
387
|
const deepLinkingUri = config.deepLinkingUri || `${config.url}/lti/deep-linking`;
|
|
@@ -319,7 +389,12 @@ export class DynamicRegistrationService {
|
|
|
319
389
|
const launchUri = config.launchUri || `${config.url}/lti/launch`;
|
|
320
390
|
const loginUri = config.loginUri || `${config.url}/lti/login`;
|
|
321
391
|
|
|
322
|
-
const messages = this.buildMessages(
|
|
392
|
+
const messages = this.buildMessages(
|
|
393
|
+
selectedServices,
|
|
394
|
+
deepLinkingUri,
|
|
395
|
+
platformFamily,
|
|
396
|
+
config.name,
|
|
397
|
+
);
|
|
323
398
|
const scopes = this.buildScopes(selectedServices);
|
|
324
399
|
|
|
325
400
|
const toolRegistrationPayload: ToolRegistrationPayload = {
|
|
@@ -173,7 +173,7 @@ export async function postRegistrationToMoodle(
|
|
|
173
173
|
if (!response.ok) {
|
|
174
174
|
const errorText = await response.json();
|
|
175
175
|
logger.error({ errorText }, 'lti dynamic registration error');
|
|
176
|
-
throw new Error(errorText);
|
|
176
|
+
throw new Error(JSON.stringify(errorText));
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
const data = await response.json();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats an unknown error into a readable string message.
|
|
3
|
+
* Handles Error objects, strings, and other types safely.
|
|
4
|
+
*
|
|
5
|
+
* @param error - The error to format (can be Error, string, or any other type)
|
|
6
|
+
* @returns Formatted error message string
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* try {
|
|
11
|
+
* await riskyOperation();
|
|
12
|
+
* } catch (error) {
|
|
13
|
+
* throw new Error(`Operation failed: ${formatError(error)}`);
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export function formatError(error: unknown): string {
|
|
18
|
+
if (error instanceof Error) {
|
|
19
|
+
return error.message;
|
|
20
|
+
}
|
|
21
|
+
return String(error);
|
|
22
|
+
}
|