npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@memberjunction/actions-bizapps-lms

v5.11.0

Published

LMS system integration actions for MemberJunction

Readme

@memberjunction/actions-bizapps-lms

Learning Management System (LMS) integration actions for MemberJunction. This package provides a standardized, multi-provider interface for interacting with LMS platforms through the MemberJunction Actions framework, currently supporting LearnWorlds with an extensible architecture for additional providers.

For general Actions framework concepts, patterns, and the "Actions as boundaries" philosophy, see the Actions README and Actions CLAUDE.md. For shared BizApps patterns (credential management, provider base classes, multi-tenant support), see the BizApps README.

Architecture

The package follows a three-tier class hierarchy: a domain-level base class providing common LMS utilities, provider-specific base classes handling API authentication and communication, and individual action classes implementing specific operations.

graph TD
    subgraph Framework["MemberJunction Actions Framework"]
        BA[BaseAction<br/>@memberjunction/actions]
    end

    subgraph Domain["Domain Layer"]
        BLMS[BaseLMSAction<br/>Credential management<br/>Progress calculation<br/>Duration formatting<br/>Enrollment status mapping]
    end

    subgraph Provider["Provider Layer"]
        LWBase[LearnWorldsBaseAction<br/>API v2 authentication<br/>Paginated requests<br/>Date format conversion<br/>Status mapping]
    end

    subgraph Actions["LearnWorlds Actions"]
        direction LR
        UM["User Management<br/>GetUsers | GetUserDetails<br/>CreateUser | GetUserProgress"]
        CM["Course Management<br/>GetCourses | GetCourseDetails<br/>GetCourseAnalytics"]
        EM["Enrollment Management<br/>EnrollUser | GetUserEnrollments<br/>UpdateUserProgress"]
        AM["Assessment & Certs<br/>GetQuizResults<br/>GetCertificates"]
    end

    BA --> BLMS
    BLMS --> LWBase
    LWBase --> UM
    LWBase --> CM
    LWBase --> EM
    LWBase --> AM

    style Framework fill:#64748b,stroke:#475569,color:#fff
    style Domain fill:#2d6a9f,stroke:#1a4971,color:#fff
    style Provider fill:#7c5295,stroke:#563a6b,color:#fff
    style Actions fill:#2d8659,stroke:#1a5c3a,color:#fff

Credential Resolution Flow

All actions resolve API credentials through a consistent two-step process, checking environment variables first with a database fallback.

flowchart LR
    A[Action invoked<br/>with CompanyID] --> B{Check env var<br/>BIZAPPS_LEARNWORLDS<br/>_CompanyID_API_KEY}
    B -- Found --> D[Use env credentials]
    B -- Not found --> C[Query CompanyIntegration<br/>entity from database]
    C --> E{APIKey or<br/>AccessToken<br/>present?}
    E -- Yes --> F[Use DB credentials]
    E -- No --> G[Throw error:<br/>No credentials found]
    D --> H[Build API request<br/>with Bearer token]
    F --> H

    style A fill:#2d6a9f,stroke:#1a4971,color:#fff
    style B fill:#b8762f,stroke:#8a5722,color:#fff
    style C fill:#7c5295,stroke:#563a6b,color:#fff
    style D fill:#2d8659,stroke:#1a5c3a,color:#fff
    style F fill:#2d8659,stroke:#1a5c3a,color:#fff
    style G fill:#64748b,stroke:#475569,color:#fff
    style H fill:#2d8659,stroke:#1a5c3a,color:#fff
    style E fill:#b8762f,stroke:#8a5722,color:#fff

Installation

npm install @memberjunction/actions-bizapps-lms

This package is part of the MemberJunction monorepo. When working within the monorepo, add the dependency to your package's package.json and run npm install at the repository root.

Setup

1. Create Integration Record

INSERT INTO Integration (Name, Description, NavigationBaseURL, ClassName)
VALUES ('LearnWorlds', 'LearnWorlds LMS Integration',
        'https://api.learnworlds.com', 'LearnWorldsIntegration');

2. Configure CompanyIntegration

INSERT INTO CompanyIntegration (CompanyID, IntegrationID, ExternalSystemID, IsActive)
VALUES (@CompanyID, @LearnWorldsIntegrationID, @SchoolDomain, 1);
-- ExternalSystemID: Your LearnWorlds school domain (e.g., 'myschool.learnworlds.com')

3. Set Environment Variables

# Format: BIZAPPS_LEARNWORLDS_{COMPANY_ID}_{CREDENTIAL_TYPE}
BIZAPPS_LEARNWORLDS_12345_API_KEY=lw_api_xxxxxxxxxxxxx
BIZAPPS_LEARNWORLDS_12345_SCHOOL_DOMAIN=myschool.learnworlds.com

Available Actions

User Management

| Action | Class | Description | |--------|-------|-------------| | Get Users | GetLearnWorldsUsersAction | List and search users with role, status, tag, and date filters | | Get User Details | GetLearnWorldsUserDetailsAction | Comprehensive user profile with enrollments, achievements, and engagement metrics | | Get User Progress | GetLearnWorldsUserProgressAction | Detailed learning progress across all courses or a specific course, with optional unit/lesson breakdown | | Create User | CreateUserAction | Create a new user with optional course enrollments and welcome email |

Course Management

| Action | Class | Description | |--------|-------|-------------| | Get Courses | GetLearnWorldsCoursesAction | Search and filter the course catalog by status, category, level, language, price range, and tags | | Get Course Details | GetLearnWorldsCourseDetailsAction | Full course information including curriculum modules, instructors, and enrollment statistics | | Get Course Analytics | GetCourseAnalyticsAction | Performance analytics with enrollment trends, engagement metrics, completion rates, and revenue data |

Enrollment Management

| Action | Class | Description | |--------|-------|-------------| | Enroll User | EnrollUserAction | Enroll a user in a course with optional pricing, start/expiry dates, and notifications | | Get User Enrollments | GetUserEnrollmentsAction | List all enrollments for a user with progress, course details, and summary statistics | | Update User Progress | UpdateUserProgressAction | Update progress at the course or individual lesson level with score and time tracking |

Assessment and Certificates

| Action | Class | Description | |--------|-------|-------------| | Get Quiz Results | GetQuizResultsAction | Retrieve quiz/assessment results with question-level details, answers, and accuracy metrics | | Get Certificates | GetCertificatesAction | Fetch earned certificates with download links, verification codes, and expiration status |

API Reference

Base Classes

BaseLMSAction

Abstract base class for all LMS actions. Provides:

  • getCompanyIntegration(companyId, contextUser) - Resolves the CompanyIntegrationEntity for the given company, with internal caching
  • getCredentialFromEnv(companyId, credentialType) - Reads credentials from environment variables using the BIZAPPS_{PROVIDER}_{COMPANY_ID}_{TYPE} pattern
  • getAPICredentials(integration) - Two-step credential resolution (environment variables first, then database)
  • calculateProgressPercentage(completed, total) - Computes progress as a rounded percentage
  • formatDuration(seconds) - Converts seconds to human-readable format (e.g., 2h 15m 30s)
  • mapEnrollmentStatus(status) - Normalizes provider-specific statuses to standard values: active, completed, expired, suspended, unknown

LearnWorldsBaseAction

LearnWorlds-specific base class extending BaseLMSAction. Provides:

  • makeLearnWorldsRequest<T>(endpoint, method, body, contextUser) - Authenticated HTTP request to the LearnWorlds v2 API with Bearer token, error parsing, and JSON handling
  • makeLearnWorldsPaginatedRequest<T>(endpoint, params, contextUser) - Auto-paginating wrapper that collects all pages of results, respecting MaxResults limits
  • parseLearnWorldsDate(dateString) - Handles both ISO strings and Unix epoch timestamps (seconds)
  • mapUserStatus(status) - Maps LearnWorlds statuses to active | inactive | suspended
  • mapLearnWorldsEnrollmentStatus(enrollment) - Determines enrollment status from boolean flags (completed, expired, suspended, active)
  • calculateProgress(progressData) - Extracts percentage, completed/total units, and time spent from LearnWorlds progress objects

Action Parameters

Every action requires the CompanyID input parameter (inherited from BaseLMSAction). Additional parameters are action-specific, documented in the sections below.

GetLearnWorldsUsersAction

Input Parameters:

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | CompanyID | string | required | MemberJunction Company ID | | SearchText | string | null | Search by name, email, or username | | Role | string | null | Filter by role (student, instructor, admin) | | Status | string | null | Filter by status (active, inactive, suspended) | | Tags | string | null | Comma-separated tag filter | | CreatedAfter | string | null | ISO date lower bound | | CreatedBefore | string | null | ISO date upper bound | | SortBy | string | created | Sort field (created, name, email, last_login) | | SortOrder | string | desc | Sort direction (asc, desc) | | IncludeCourseStats | boolean | true | Include enrollment statistics per user | | MaxResults | number | 100 | Maximum number of users to return |

Output Parameters:

| Parameter | Type | Description | |-----------|------|-------------| | Users | LearnWorldsUser[] | Array of user objects with profile and course statistics | | TotalCount | number | Total number of users returned | | Summary | object | Aggregate statistics including role distribution, most active users, and recent signups |

GetLearnWorldsUserDetailsAction

Input Parameters:

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | CompanyID | string | required | MemberJunction Company ID | | UserID | string | required | LearnWorlds user ID | | IncludeEnrollments | boolean | true | Include course enrollment details | | IncludeStats | boolean | true | Include additional learning statistics |

Output Parameters:

| Parameter | Type | Description | |-----------|------|-------------| | UserDetails | LearnWorldsUserDetails | Complete user profile with learning data, achievements, and account settings | | Summary | object | Engagement overview with learning progress, achievement counts, and recent activity |

GetLearnWorldsUserProgressAction

Input Parameters:

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | CompanyID | string | required | MemberJunction Company ID | | UserID | string | required | LearnWorlds user ID | | CourseID | string | null | Optional specific course ID (omit for all courses) | | IncludeUnitDetails | boolean | false | Include module/unit level breakdown | | IncludeLessonDetails | boolean | false | Include individual lesson progress |

Output Parameters:

| Parameter | Type | Description | |-----------|------|-------------| | UserProgress | UserLearningProgress | Comprehensive progress data across all courses or a specific course | | Summary | object | Overview with completion rate, current focus courses, and achievement data |

GetLearnWorldsCoursesAction

Input Parameters:

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | CompanyID | string | required | MemberJunction Company ID | | SearchText | string | null | Search in title and description | | Status | string | null | Filter by status (published, draft, coming_soon) | | CategoryID | string | null | Filter by category ID | | Level | string | null | Filter by difficulty (beginner, intermediate, advanced) | | Language | string | null | Filter by course language | | OnlyFree | boolean | false | Show only free courses | | MinPrice | number | null | Minimum price filter | | MaxPrice | number | null | Maximum price filter | | Tags | string | null | Comma-separated tag filter | | InstructorID | string | null | Filter by instructor | | CreatedAfter | string | null | ISO date lower bound | | CreatedBefore | string | null | ISO date upper bound | | SortBy | string | created | Sort field (created, title, price, enrollments) | | SortOrder | string | desc | Sort direction | | IncludeEnrollmentStats | boolean | true | Include enrollment data | | MaxResults | number | 100 | Maximum results to return |

Output Parameters:

| Parameter | Type | Description | |-----------|------|-------------| | Courses | LearnWorldsCourse[] | Array of course objects with full details | | TotalCount | number | Total courses returned | | Summary | CourseCatalogSummary | Catalog statistics with category/level/language counts, enrollment stats, and pricing |

GetLearnWorldsCourseDetailsAction

Input Parameters:

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | CompanyID | string | required | MemberJunction Company ID | | CourseID | string | required | LearnWorlds course ID | | IncludeModules | boolean | true | Include curriculum structure | | IncludeInstructors | boolean | true | Include instructor profiles | | IncludeStats | boolean | true | Include enrollment and completion statistics |

Output Parameters:

| Parameter | Type | Description | |-----------|------|-------------| | CourseDetails | object | Full course information including curriculum modules, instructor profiles, and stats | | Summary | object | Key metrics overview |

EnrollUserAction

Input Parameters:

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | CompanyID | string | required | MemberJunction Company ID | | UserID | string | required | LearnWorlds user ID | | CourseID | string | required | Course to enroll in | | Price | number | 0 | Enrollment price | | Justification | string | API Enrollment | Reason for enrollment | | NotifyUser | boolean | true | Send enrollment notification email | | StartDate | string | null | Optional enrollment start date (ISO format) | | ExpiryDate | string | null | Optional enrollment expiry date (ISO format) |

Output Parameters:

| Parameter | Type | Description | |-----------|------|-------------| | EnrollmentDetails | object | Enrollment record with progress tracking and certificate eligibility | | Summary | object | Enrollment summary with user and course names |

GetUserEnrollmentsAction

Input Parameters:

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | CompanyID | string | required | MemberJunction Company ID | | UserID | string | required | LearnWorlds user ID | | Status | string | null | Filter by enrollment status | | IncludeExpired | boolean | false | Include expired enrollments | | IncludeCourseDetails | boolean | true | Include course metadata for each enrollment | | SortBy | string | enrolled_at | Sort field | | SortOrder | string | desc | Sort direction | | MaxResults | number | 100 | Maximum results |

Output Parameters:

| Parameter | Type | Description | |-----------|------|-------------| | Enrollments | array | Enrollment records with progress, grade, and certificate data | | TotalCount | number | Total enrollments found | | Summary | object | Aggregate statistics by status, average progress, and total time spent |

UpdateUserProgressAction

Input Parameters:

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | CompanyID | string | required | MemberJunction Company ID | | UserID | string | required | LearnWorlds user ID | | CourseID | string | required | Course ID | | LessonID | string | null | Optional lesson ID for lesson-level updates | | ProgressPercentage | number | null | New progress percentage | | Completed | boolean | null | Mark as completed | | TimeSpent | number | null | Additional time spent (seconds) | | Score | number | null | Score for quiz/assignment | | Notes | string | null | Progress notes |

Output Parameters:

| Parameter | Type | Description | |-----------|------|-------------| | ProgressDetails | object | Previous and updated progress state with diff | | Summary | object | Change summary including whether progress increased |

GetCourseAnalyticsAction

Input Parameters:

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | CompanyID | string | required | MemberJunction Company ID | | CourseID | string | required | Course ID | | DateFrom | string | null | Analytics period start date | | DateTo | string | null | Analytics period end date | | IncludeUserBreakdown | boolean | false | Include per-user progress distribution | | IncludeModuleStats | boolean | true | Include module/lesson-level statistics | | IncludeRevenue | boolean | true | Include revenue analytics |

Output Parameters:

| Parameter | Type | Description | |-----------|------|-------------| | CourseAnalytics | object | Comprehensive analytics: enrollment trends, progress distribution, engagement metrics, quiz performance, and revenue | | Summary | object | Key metrics and growth/engagement trends |

GetCertificatesAction

Input Parameters:

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | CompanyID | string | required | MemberJunction Company ID | | UserID | string | null | Filter by user (either UserID or CourseID required) | | CourseID | string | null | Filter by course (either UserID or CourseID required) | | DateFrom | string | null | Issued after date | | DateTo | string | null | Issued before date | | IncludeDownloadLinks | boolean | true | Include PDF and image download URLs | | SortBy | string | issued_at | Sort field | | SortOrder | string | desc | Sort direction | | MaxResults | number | 100 | Maximum results |

Output Parameters:

| Parameter | Type | Description | |-----------|------|-------------| | Certificates | array | Certificate records with user/course info, download links, and verification data | | TotalCount | number | Total certificates found | | Summary | object | Active/expired counts with optional grouping by course or user |

GetQuizResultsAction

Input Parameters:

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | CompanyID | string | required | MemberJunction Company ID | | UserID | string | null | Filter by user (at least one of UserID/CourseID/QuizID required) | | CourseID | string | null | Filter by course | | QuizID | string | null | Filter by specific quiz | | IncludeQuestions | boolean | true | Include question text and metadata | | IncludeAnswers | boolean | true | Include user answers with correct/incorrect analysis | | PassingOnly | boolean | false | Return only passing attempts | | DateFrom | string | null | Completed after date | | DateTo | string | null | Completed before date | | SortBy | string | completed_at | Sort field | | SortOrder | string | desc | Sort direction | | MaxResults | number | 100 | Maximum results |

Output Parameters:

| Parameter | Type | Description | |-----------|------|-------------| | QuizResults | array | Quiz attempts with scores, answers, accuracy metrics, and per-question breakdown | | TotalCount | number | Total results found | | Summary | object | Pass rate, average score, average duration, and per-quiz breakdown |

Exported Interfaces

The package exports several TypeScript interfaces for strong typing:

  • LearnWorldsUser - User profile with status, role, tags, custom fields, and course statistics
  • LearnWorldsUserDetails - Extended user profile with enrollment details, achievements, and account settings
  • UserEnrollment - Individual course enrollment with progress, grade, and certificate data
  • CourseProgress - Detailed progress for a single course including unit-level breakdown
  • UnitProgress - Progress within a course module/section
  • LessonProgress - Individual lesson progress with type-specific fields (video watch time, quiz scores, assignment grades)
  • UserLearningProgress - Aggregated learning progress across all courses with analytics
  • LearnWorldsCourse - Full course metadata including pricing, structure, enrollment data, and learning outcomes
  • CourseCatalogSummary - Catalog-level statistics with category, level, language, enrollment, and price breakdowns

Usage Examples

Get Active Students with Course Statistics

import { ActionEngineServer } from '@memberjunction/actions';

const engine = ActionEngineServer.Instance;
const result = await engine.RunAction({
    ActionName: 'GetLearnWorldsUsersAction',
    Params: [
        { Name: 'CompanyID', Value: 'company-123' },
        { Name: 'Role', Value: 'student' },
        { Name: 'Status', Value: 'active' },
        { Name: 'IncludeCourseStats', Value: true },
        { Name: 'SortBy', Value: 'last_login' },
        { Name: 'SortOrder', Value: 'desc' }
    ],
    ContextUser: currentUser
});

if (result.Success) {
    const users = result.Params.find(p => p.Name === 'Users')?.Value;
    const summary = result.Params.find(p => p.Name === 'Summary')?.Value;
    console.log(`Found ${users.length} active students`);
    console.log(`Average courses per student: ${summary.averageCoursesPerUser}`);
}

Track User Learning Progress

const result = await engine.RunAction({
    ActionName: 'GetLearnWorldsUserProgressAction',
    Params: [
        { Name: 'CompanyID', Value: 'company-123' },
        { Name: 'UserID', Value: 'user-456' },
        { Name: 'IncludeUnitDetails', Value: true },
        { Name: 'IncludeLessonDetails', Value: true }
    ],
    ContextUser: currentUser
});

if (result.Success) {
    const progress = result.Params.find(p => p.Name === 'UserProgress')?.Value;
    console.log(`Completed ${progress.coursesCompleted} of ${progress.totalCourses} courses`);
    console.log(`Overall progress: ${progress.overallProgressPercentage}%`);

    for (const course of progress.courses) {
        if (course.status === 'in_progress') {
            console.log(`  ${course.courseTitle}: ${course.progressPercentage}%`);
        }
    }
}

Search Course Catalog

const result = await engine.RunAction({
    ActionName: 'GetLearnWorldsCoursesAction',
    Params: [
        { Name: 'CompanyID', Value: 'company-123' },
        { Name: 'SearchText', Value: 'JavaScript' },
        { Name: 'Status', Value: 'published' },
        { Name: 'Level', Value: 'beginner' },
        { Name: 'MaxPrice', Value: 100 }
    ],
    ContextUser: currentUser
});

if (result.Success) {
    const courses = result.Params.find(p => p.Name === 'Courses')?.Value;
    for (const course of courses) {
        console.log(`${course.title} - $${course.price} (${course.totalEnrollments} students)`);
    }
}

Create User and Enroll in Courses

const result = await engine.RunAction({
    ActionName: 'CreateUserAction',
    Params: [
        { Name: 'CompanyID', Value: 'company-123' },
        { Name: 'Email', Value: '[email protected]' },
        { Name: 'FirstName', Value: 'Jane' },
        { Name: 'LastName', Value: 'Smith' },
        { Name: 'Role', Value: 'student' },
        { Name: 'SendWelcomeEmail', Value: true },
        { Name: 'EnrollInCourses', Value: ['course-101', 'course-202'] }
    ],
    ContextUser: currentUser
});

if (result.Success) {
    const summary = result.Params.find(p => p.Name === 'Summary')?.Value;
    console.log(`Created user: ${summary.email}`);
    console.log(`Enrolled in ${summary.coursesEnrolled} of ${summary.totalCoursesRequested} courses`);
}

Adding New Providers

The package is designed for extensibility. To add support for a new LMS provider:

  1. Create a provider directory: src/providers/{provider-name}/
  2. Create a provider base class extending BaseLMSAction with provider-specific authentication and API communication
  3. Implement individual action classes extending the provider base class
  4. Map provider-specific data formats to the shared interfaces (LearnWorldsUser, CourseProgress, etc.)
  5. Export everything from provider and actions index files
  6. Re-export the new provider from src/index.ts
src/
  base/
    base-lms.action.ts          # Domain base class (shared)
  providers/
    learnworlds/                # Existing provider
      learnworlds-base.action.ts
      actions/
        get-users.action.ts
        ...
    moodle/                     # New provider
      moodle-base.action.ts
      actions/
        get-users.action.ts
        ...
  index.ts

Dependencies

| Package | Purpose | |---------|---------| | @memberjunction/core | UserInfo, Metadata, RunView for entity access | | @memberjunction/global | @RegisterClass decorator for action discovery | | @memberjunction/actions-base | ActionParam, ActionResultSimple, RunActionParams types | | @memberjunction/actions | BaseAction class that all actions extend | | @memberjunction/core-entities | CompanyIntegrationEntity for credential/integration management |

Related Packages

Building

# Build this package
cd packages/Actions/BizApps/LMS
npm run build

# Watch mode for development
npm run watch