@lti-tool/core 1.0.0 → 1.0.2
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/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/services/session.service.d.ts.map +1 -1
- package/dist/services/session.service.js +27 -20
- package/dist/utils/environment.d.ts +14 -0
- package/dist/utils/environment.d.ts.map +1 -0
- package/dist/utils/environment.js +30 -0
- package/package.json +4 -4
- package/src/index.ts +1 -0
- package/src/services/session.service.ts +30 -16
- package/src/utils/environment.ts +33 -0
package/CHANGELOG.md
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.service.d.ts","sourceRoot":"","sources":["../../src/services/session.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"session.service.d.ts","sourceRoot":"","sources":["../../src/services/session.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAU3D;;;;;;GAMG;AAEH,wBAAgB,aAAa,CAAC,eAAe,EAAE,eAAe,GAAG,UAAU,CAkG1E"}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
const ROLE_MAPPINGS = {
|
|
2
|
+
Instructor: 'instructor',
|
|
3
|
+
Learner: 'student',
|
|
4
|
+
Administrator: 'admin',
|
|
5
|
+
ContentDeveloper: 'content-developer',
|
|
6
|
+
Member: 'member',
|
|
7
|
+
};
|
|
1
8
|
/**
|
|
2
9
|
* Creates an LTI session object from a validated LTI 1.3 JWT payload.
|
|
3
10
|
* Extracts user information, context data, and available services into a structured session.
|
|
@@ -5,7 +12,7 @@
|
|
|
5
12
|
* @param lti13JwtPayload - Validated LTI 1.3 JWT payload from successful launch
|
|
6
13
|
* @returns Complete LTI session object with user, context, and service information
|
|
7
14
|
*/
|
|
8
|
-
// oxlint-disable-next-line max-lines-per-function
|
|
15
|
+
// oxlint-disable-next-line max-lines-per-function complexity -- flat data mapping
|
|
9
16
|
export function createSession(lti13JwtPayload) {
|
|
10
17
|
const roles = lti13JwtPayload['https://purl.imsglobal.org/spec/lti/claim/roles'] || [];
|
|
11
18
|
const context = lti13JwtPayload['https://purl.imsglobal.org/spec/lti/claim/context'];
|
|
@@ -15,9 +22,9 @@ export function createSession(lti13JwtPayload) {
|
|
|
15
22
|
const agsEndpoint = lti13JwtPayload['https://purl.imsglobal.org/spec/lti-ags/claim/endpoint'];
|
|
16
23
|
const nrpsService = lti13JwtPayload['https://purl.imsglobal.org/spec/lti-nrps/claim/namesroleservice'];
|
|
17
24
|
const deepLinkingSettings = lti13JwtPayload['https://purl.imsglobal.org/spec/lti-dl/claim/deep_linking_settings'];
|
|
18
|
-
const isInstructor = roles
|
|
19
|
-
const isStudent = roles
|
|
20
|
-
const isAdmin = roles
|
|
25
|
+
const isInstructor = hasRole(roles, 'Instructor');
|
|
26
|
+
const isStudent = hasRole(roles, 'Learner');
|
|
27
|
+
const isAdmin = hasRole(roles, 'Administrator');
|
|
21
28
|
const services = {};
|
|
22
29
|
if (agsEndpoint) {
|
|
23
30
|
let lineItemUrl;
|
|
@@ -48,21 +55,7 @@ export function createSession(lti13JwtPayload) {
|
|
|
48
55
|
};
|
|
49
56
|
}
|
|
50
57
|
// Extract simplified roles
|
|
51
|
-
const simplifiedRoles =
|
|
52
|
-
for (const role of roles) {
|
|
53
|
-
if (role.includes('Instructor'))
|
|
54
|
-
simplifiedRoles.push('instructor');
|
|
55
|
-
if (role.includes('Learner'))
|
|
56
|
-
simplifiedRoles.push('student');
|
|
57
|
-
if (role.includes('Administrator'))
|
|
58
|
-
simplifiedRoles.push('admin');
|
|
59
|
-
if (role.includes('ContentDeveloper'))
|
|
60
|
-
simplifiedRoles.push('content-developer');
|
|
61
|
-
if (role.includes('Member'))
|
|
62
|
-
simplifiedRoles.push('member');
|
|
63
|
-
}
|
|
64
|
-
// Remove duplicates
|
|
65
|
-
const uniqueRoles = [...new Set(simplifiedRoles)];
|
|
58
|
+
const simplifiedRoles = simplifyRoles(roles);
|
|
66
59
|
return {
|
|
67
60
|
jwtPayload: lti13JwtPayload,
|
|
68
61
|
id: crypto.randomUUID(),
|
|
@@ -72,7 +65,7 @@ export function createSession(lti13JwtPayload) {
|
|
|
72
65
|
email: lti13JwtPayload.email,
|
|
73
66
|
familyName: lti13JwtPayload.family_name,
|
|
74
67
|
givenName: lti13JwtPayload.given_name,
|
|
75
|
-
roles:
|
|
68
|
+
roles: simplifiedRoles,
|
|
76
69
|
},
|
|
77
70
|
context: {
|
|
78
71
|
id: context?.id || '',
|
|
@@ -106,3 +99,17 @@ export function createSession(lti13JwtPayload) {
|
|
|
106
99
|
isNameAndRolesAvailable: !!nrpsService,
|
|
107
100
|
};
|
|
108
101
|
}
|
|
102
|
+
function simplifyRoles(roles) {
|
|
103
|
+
const simplified = new Set();
|
|
104
|
+
for (const role of roles) {
|
|
105
|
+
for (const [key, value] of Object.entries(ROLE_MAPPINGS)) {
|
|
106
|
+
if (role.includes(key)) {
|
|
107
|
+
simplified.add(value);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return [...simplified];
|
|
112
|
+
}
|
|
113
|
+
function hasRole(roles, pattern) {
|
|
114
|
+
return roles.some((role) => role.includes(pattern));
|
|
115
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects if the application is running in a serverless environment.
|
|
3
|
+
*
|
|
4
|
+
* Checks for environment variables commonly set by serverless platforms:
|
|
5
|
+
* - AWS Lambda: AWS_LAMBDA_FUNCTION_NAME, AWS_EXECUTION_ENV, LAMBDA_TASK_ROOT
|
|
6
|
+
* - Google Cloud Functions/Run: FUNCTION_NAME, FUNCTION_TARGET, K_SERVICE
|
|
7
|
+
* - Azure Functions: FUNCTIONS_WORKER_RUNTIME, AZURE_FUNCTIONS_ENVIRONMENT
|
|
8
|
+
* - Vercel: VERCEL, VERCEL_ENV
|
|
9
|
+
* - Netlify Functions: NETLIFY
|
|
10
|
+
*
|
|
11
|
+
* @returns true if serverless environment detected, false otherwise
|
|
12
|
+
*/
|
|
13
|
+
export declare function isServerlessEnvironment(): boolean;
|
|
14
|
+
//# sourceMappingURL=environment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../../src/utils/environment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,IAAI,OAAO,CAoBjD"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects if the application is running in a serverless environment.
|
|
3
|
+
*
|
|
4
|
+
* Checks for environment variables commonly set by serverless platforms:
|
|
5
|
+
* - AWS Lambda: AWS_LAMBDA_FUNCTION_NAME, AWS_EXECUTION_ENV, LAMBDA_TASK_ROOT
|
|
6
|
+
* - Google Cloud Functions/Run: FUNCTION_NAME, FUNCTION_TARGET, K_SERVICE
|
|
7
|
+
* - Azure Functions: FUNCTIONS_WORKER_RUNTIME, AZURE_FUNCTIONS_ENVIRONMENT
|
|
8
|
+
* - Vercel: VERCEL, VERCEL_ENV
|
|
9
|
+
* - Netlify Functions: NETLIFY
|
|
10
|
+
*
|
|
11
|
+
* @returns true if serverless environment detected, false otherwise
|
|
12
|
+
*/
|
|
13
|
+
export function isServerlessEnvironment() {
|
|
14
|
+
return !!(
|
|
15
|
+
// AWS Lambda
|
|
16
|
+
(process.env.AWS_LAMBDA_FUNCTION_NAME ||
|
|
17
|
+
process.env.AWS_EXECUTION_ENV ||
|
|
18
|
+
process.env.LAMBDA_TASK_ROOT ||
|
|
19
|
+
// Google Cloud Functions / Cloud Run
|
|
20
|
+
process.env.FUNCTION_NAME ||
|
|
21
|
+
process.env.FUNCTION_TARGET ||
|
|
22
|
+
process.env.K_SERVICE ||
|
|
23
|
+
// Azure Functions
|
|
24
|
+
process.env.FUNCTIONS_WORKER_RUNTIME ||
|
|
25
|
+
process.env.AZURE_FUNCTIONS_ENVIRONMENT ||
|
|
26
|
+
// Vercel
|
|
27
|
+
process.env.VERCEL ||
|
|
28
|
+
// Netlify Functions
|
|
29
|
+
process.env.NETLIFY));
|
|
30
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lti-tool/core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "LTI 1.3 implementation for Node.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lti",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"test": "vitest"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"jose": "^6.1.
|
|
40
|
-
"zod": "^4.
|
|
39
|
+
"jose": "^6.1.3",
|
|
40
|
+
"zod": "^4.3.5"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"pino": "^9.11.0"
|
|
@@ -48,6 +48,6 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"pino": "^10.
|
|
51
|
+
"pino": "^10.2.0"
|
|
52
52
|
}
|
|
53
53
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import type { LTISession } from '../interfaces/ltiSession.js';
|
|
2
2
|
import type { LTI13JwtPayload } from '../schemas/index.js';
|
|
3
3
|
|
|
4
|
+
const ROLE_MAPPINGS: Record<string, string> = {
|
|
5
|
+
Instructor: 'instructor',
|
|
6
|
+
Learner: 'student',
|
|
7
|
+
Administrator: 'admin',
|
|
8
|
+
ContentDeveloper: 'content-developer',
|
|
9
|
+
Member: 'member',
|
|
10
|
+
};
|
|
11
|
+
|
|
4
12
|
/**
|
|
5
13
|
* Creates an LTI session object from a validated LTI 1.3 JWT payload.
|
|
6
14
|
* Extracts user information, context data, and available services into a structured session.
|
|
@@ -8,7 +16,7 @@ import type { LTI13JwtPayload } from '../schemas/index.js';
|
|
|
8
16
|
* @param lti13JwtPayload - Validated LTI 1.3 JWT payload from successful launch
|
|
9
17
|
* @returns Complete LTI session object with user, context, and service information
|
|
10
18
|
*/
|
|
11
|
-
// oxlint-disable-next-line max-lines-per-function
|
|
19
|
+
// oxlint-disable-next-line max-lines-per-function complexity -- flat data mapping
|
|
12
20
|
export function createSession(lti13JwtPayload: LTI13JwtPayload): LTISession {
|
|
13
21
|
const roles = lti13JwtPayload['https://purl.imsglobal.org/spec/lti/claim/roles'] || [];
|
|
14
22
|
const context = lti13JwtPayload['https://purl.imsglobal.org/spec/lti/claim/context'];
|
|
@@ -25,9 +33,9 @@ export function createSession(lti13JwtPayload: LTI13JwtPayload): LTISession {
|
|
|
25
33
|
const deepLinkingSettings =
|
|
26
34
|
lti13JwtPayload['https://purl.imsglobal.org/spec/lti-dl/claim/deep_linking_settings'];
|
|
27
35
|
|
|
28
|
-
const isInstructor = roles
|
|
29
|
-
const isStudent = roles
|
|
30
|
-
const isAdmin = roles
|
|
36
|
+
const isInstructor = hasRole(roles, 'Instructor');
|
|
37
|
+
const isStudent = hasRole(roles, 'Learner');
|
|
38
|
+
const isAdmin = hasRole(roles, 'Administrator');
|
|
31
39
|
|
|
32
40
|
const services: Record<string, unknown> = {};
|
|
33
41
|
if (agsEndpoint) {
|
|
@@ -61,17 +69,7 @@ export function createSession(lti13JwtPayload: LTI13JwtPayload): LTISession {
|
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
// Extract simplified roles
|
|
64
|
-
const simplifiedRoles
|
|
65
|
-
for (const role of roles) {
|
|
66
|
-
if (role.includes('Instructor')) simplifiedRoles.push('instructor');
|
|
67
|
-
if (role.includes('Learner')) simplifiedRoles.push('student');
|
|
68
|
-
if (role.includes('Administrator')) simplifiedRoles.push('admin');
|
|
69
|
-
if (role.includes('ContentDeveloper')) simplifiedRoles.push('content-developer');
|
|
70
|
-
if (role.includes('Member')) simplifiedRoles.push('member');
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Remove duplicates
|
|
74
|
-
const uniqueRoles = [...new Set(simplifiedRoles)];
|
|
72
|
+
const simplifiedRoles = simplifyRoles(roles);
|
|
75
73
|
|
|
76
74
|
return {
|
|
77
75
|
jwtPayload: lti13JwtPayload,
|
|
@@ -82,7 +80,7 @@ export function createSession(lti13JwtPayload: LTI13JwtPayload): LTISession {
|
|
|
82
80
|
email: lti13JwtPayload.email,
|
|
83
81
|
familyName: lti13JwtPayload.family_name,
|
|
84
82
|
givenName: lti13JwtPayload.given_name,
|
|
85
|
-
roles:
|
|
83
|
+
roles: simplifiedRoles,
|
|
86
84
|
},
|
|
87
85
|
context: {
|
|
88
86
|
id: context?.id || '',
|
|
@@ -118,3 +116,19 @@ export function createSession(lti13JwtPayload: LTI13JwtPayload): LTISession {
|
|
|
118
116
|
isNameAndRolesAvailable: !!nrpsService,
|
|
119
117
|
};
|
|
120
118
|
}
|
|
119
|
+
|
|
120
|
+
function simplifyRoles(roles: string[]): string[] {
|
|
121
|
+
const simplified = new Set<string>();
|
|
122
|
+
for (const role of roles) {
|
|
123
|
+
for (const [key, value] of Object.entries(ROLE_MAPPINGS)) {
|
|
124
|
+
if (role.includes(key)) {
|
|
125
|
+
simplified.add(value);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return [...simplified];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function hasRole(roles: string[], pattern: string): boolean {
|
|
133
|
+
return roles.some((role) => role.includes(pattern));
|
|
134
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects if the application is running in a serverless environment.
|
|
3
|
+
*
|
|
4
|
+
* Checks for environment variables commonly set by serverless platforms:
|
|
5
|
+
* - AWS Lambda: AWS_LAMBDA_FUNCTION_NAME, AWS_EXECUTION_ENV, LAMBDA_TASK_ROOT
|
|
6
|
+
* - Google Cloud Functions/Run: FUNCTION_NAME, FUNCTION_TARGET, K_SERVICE
|
|
7
|
+
* - Azure Functions: FUNCTIONS_WORKER_RUNTIME, AZURE_FUNCTIONS_ENVIRONMENT
|
|
8
|
+
* - Vercel: VERCEL, VERCEL_ENV
|
|
9
|
+
* - Netlify Functions: NETLIFY
|
|
10
|
+
*
|
|
11
|
+
* @returns true if serverless environment detected, false otherwise
|
|
12
|
+
*/
|
|
13
|
+
export function isServerlessEnvironment(): boolean {
|
|
14
|
+
return !!(
|
|
15
|
+
// AWS Lambda
|
|
16
|
+
(
|
|
17
|
+
process.env.AWS_LAMBDA_FUNCTION_NAME ||
|
|
18
|
+
process.env.AWS_EXECUTION_ENV ||
|
|
19
|
+
process.env.LAMBDA_TASK_ROOT ||
|
|
20
|
+
// Google Cloud Functions / Cloud Run
|
|
21
|
+
process.env.FUNCTION_NAME ||
|
|
22
|
+
process.env.FUNCTION_TARGET ||
|
|
23
|
+
process.env.K_SERVICE ||
|
|
24
|
+
// Azure Functions
|
|
25
|
+
process.env.FUNCTIONS_WORKER_RUNTIME ||
|
|
26
|
+
process.env.AZURE_FUNCTIONS_ENVIRONMENT ||
|
|
27
|
+
// Vercel
|
|
28
|
+
process.env.VERCEL ||
|
|
29
|
+
// Netlify Functions
|
|
30
|
+
process.env.NETLIFY
|
|
31
|
+
)
|
|
32
|
+
);
|
|
33
|
+
}
|