@faiber/faiber-lms

中文文档
2.8.0发布于 2026/11/14TypeScriptES Module近 30 天下载 3761

Educational data transfer

原始使用文档
文档目录24 节
  1. Educational data transfer
  2. Install
  3. Configure
  4. Complete capability
  5. Homework and exam banks
  6. Classroom weekly schedules
  7. Authentication and authorization
  8. Inputs, queries, responses, and transport
  9. Errors and cancellation
  10. Versioned interactive learning
  11. Question, option, homework, and course images
  12. Makeup sessions for selected students
  13. Teacher and support filters
  14. Student notification settings and assigned chat contacts (0…
  15. Summary API migration in 1.0.0
  16. Classroom student counts
  17. Academy completion and exam submission
  18. Supported wire behavior
  19. Classroom short IDs and search
  20. Complete weekly calendar and today view
  21. Calendar contract
  22. Confirm classroom course or schedule changes
  23. Certificate filters and session media
  24. Private lobby displays

@faiber/faiber-lms

Educational data transfer

const result = await lms.transferUserData(
  { from_user_id: sourceIdpUuid, to_user_id: destinationIdpUuid },
  crypto.randomUUID(), // retain this key and reuse it if this request needs retrying
);
console.log(result.data.data.transfer_id, result.data.data.moved);

This supported POST /api/v1/idp/option/transfer-user-data requires user_data:transfer and an Idempotency-Key header (1–128 printable ASCII characters, no spaces). Both IDs must be distinct, non-nil UUIDs with active Profile records. TransferUserDataInput, TransferUserDataResponse, EducationalTransferCounts, and TransferUserDataErrorResponse are exported types.

The transaction changes learner ownership of classroom enrollments, attendance, homework assignments, exam attempts, grades, certificates, classroom notes/evaluations, academy enrollments, and interactive runs. For session evaluations it changes the target learner of teacher ratings and the author of student feedback. Record IDs, status, scores, submissions, question snapshots and foreign-key links are preserved; academy progress/attempts, exam answers and interactive attempts therefore follow their unchanged parent records. Response moved counts directly updated rows by category, not linked children. Profile classroom-cache synchronization is queued in the same transaction (profile_sync: "queued").

It does not change Office balances, wallets, invoices, transactions or installments; identity/Profile properties; teachers, consultants, support staff or certificate issuers; invitations, support interactions, club project ownership or work-time entries; Chat participants, messages or conversation IDs; or historical migration/audit attribution. This is an LMS learner-record transfer, not an account merge. Existing Chat conversation access is not transferred.

Same actor + same key + same IDs replays the original successful response. Reusing that key with different IDs returns 409 idempotency_conflict. Existing destination uniqueness conflicts return 409 destination_conflict without merging or deleting records. Started exam attempts return 409 active_attempt; busy educational writes can return 409 transfer_busy (retry with the same key). Missing/invalid IDs or key return 422; unavailable Profile validation returns 502. Failures leave all educational records unchanged and do not consume the key. Authentication/permission failures use the shared IDP error contract.

Successful response: {status:"success", message:"Educational data transferred", data:{transfer_id,from_user_id,to_user_id,moved,profile_sync:"queued"}}.

Validation/conflict response: {status:"error", error:{code:"destination_conflict",message:"...",details:{}}}. user_not_found includes details.user_id. Axios errors retain the HTTP status and typed response body.

Courses, classrooms, homework/project banks and assignments, exam banks/items/sessions/users, certificates, reports, and education configuration.

Install

npm install @faiber/faiber-lms

Configure

import { FaiberClient, MemoryTokenProvider } from "@faiber/sdk-core";
import { LmsApi } from "@faiber/faiber-lms";

const tokens = new MemoryTokenProvider();
const client = new FaiberClient("lms", {
  domains: { lms: process.env.FAIBER_LMS_URL! },
  tokenProvider: tokens,
  axios: { timeout: 15_000, withCredentials: true },
});
const api = new LmsApi(client);

const courses = await api.courses.list({
  "page[number]": 1,
  "page[size]": 20,
});
const sessions = await api.courseSessions(courseId);

Complete capability

This package exposes 181 registered operations through typed bindings, including notification rules/contacts and excluding the eight retired LMS summary operations. Binding counts describe inventory only; service permissions and workflow verification remain separate requirements. Common workflows use concise methods, while named operation clients retain exact wire contracts.

AreaOperationsHTTP methods
academy8GET, POST
branding5GET, POST, PUT
certificate10GET, PATCH, POST
classroom20DELETE, GET, PATCH, POST, PUT
config20GET, PATCH, POST
contacts1GET
course17DELETE, GET, PATCH, POST
dashboard1GET
docs1GET
drm-routes1GET
evaluation4GET, POST, PUT
exam22DELETE, GET, PATCH, POST, PUT
homework25DELETE, GET, PATCH, POST
integration7GET, POST
interactive15GET, POST, PUT
media2GET, POST
mod3GET, POST, PUT
notifications1POST
profile-routes2GET
report9GET
router4GET
service1GET
session1GET
transfer1POST

Course sessions, classroom users and absences, homework assignments, exam attempts, academy enrollments, reports, and certificate workflows have dedicated typed operations. Most LMS updates use PATCH; unsupported generic deletes are guarded locally.

Homework assignments and exam sessions expose explicit student_user_id, teacher_user_id, consultant_user_id, and support_user_id list filters. The existing user_id filter remains available when a caller wants any related participant or staff role.

Homework-bank items accept name on create and update. Responses always include name; when it is blank or omitted, the LMS derives it from the question text and caps it at 80 Unicode characters with an ellipsis.

Homework and exam banks

Reusable homework definitions are the homework-bank layer: api.homeworkBanks and the compatibility alias api.homeworks address the same records. Use api.homeworkBankItems(bankId) for the definition's questions/items (homework_questions). Every item has kind: "todo" | "project"; the nested list accepts the same kind filter, and create/update accept the field. Prefer api.homeworkAssignments.createForItem(item, delivery) to select the exercise ID explicitly. Delivery records reference an item's ID (not the bank ID) and are fully managed through api.homeworkAssignments. The compatibility methods listAssignments, createAssignment, assignment, updateAssignment, and deleteAssignment remain available.

Exam definitions are exposed as api.examBanks, their questions/items as api.examBankItems, delivery sessions as api.examSessions, and learner attempts as listExamUsers, examUser, and updateExamUser. The legacy api.exams, api.examQuestions, and api.homeworks names remain available.

const bank = await api.homeworkBanks.create({ name: "Projects", status: "active" });
const items = api.homeworkBankItems(bank.data.data.id);
const item = await items.create({ question_text: "Build a recursion demo", question_type: "answer", kind: "project", status: "active" });
// item.data.data.id is the exercise; item.data.data.homework_id is its parent bank.
const delivery = await api.homeworkAssignments.createForItem(item.data.data, {
  user_id: studentUserId,
  classroom_id: classroomId,
  status: "pending",
});
const todos = await items.list({ kind: "todo" });
const assignments = await api.homeworkAssignments.list({
  statuses: ["pending", "unsolved", "completed"],
  homework_id: item.data.data.id,
});

Session attendance rows expose attendance_mode (online or in_person) alongside the stored online boolean, attended seconds, description, and ratings. The same details are available on absence rows. When recording an attendance batch, include online for each learner whose attendance mode matters; omitting it defaults to in-person.

Classroom weekly schedules

Use day_of_week (Sunday 0 through Saturday 6) and starts_at (HH:MM or HH:MM:SS) for new classroom schedules. Set timezone_offset_minutes when the local time differs from the offset in the classroom's starts_at timestamp.

await api.classrooms.create({
  course_id: courseId,
  name: "Autumn class",
  starts_at: "2026-09-20T09:08:11.383Z",
  status: "active",
  weekly_schedule: [
    { day_of_week: 0, starts_at: "15:00", timezone_offset_minutes: 210, mode: "online" },
    { day_of_week: 2, starts_at: "14:00", timezone_offset_minutes: 210, mode: "interactive" },
  ],
});

The weekly mode selects each generated primary session type: online → ONLINE_CLASS, interactive → VIDEO (composition media), in_person → MEETING, and makeup → REWIEW_CLASS. You can instead supply a course_session_type_id. Updating weekly_schedule on a classroom also reschedules its existing generated sessions. For existing forms, the LMS accepts day (Persian or English weekday name) with start_time; it normalizes these fields on save. Legacy delivery_type and session_type are supported when mode is absent, with delivery_type taking precedence. A Persian day without an explicit offset defaults to Tehran time (+03:30).

Complementary videos inherit the time of the preceding session in course sort_order, including a preceding video or online class. Moving that session also moves its following complementary videos. Opt in to shifting later primary sessions onto the next weekly schedule slots after the new start time:

await api.classroomSessions.update(sessionId, {
  starts_at: "2026-09-23T10:00:00Z",
  shift_following_sessions: true,
});

If the selected session or an affected later session already has a provisioned room, the LMS returns 409 recording_reset_confirmation_required. Repeat the request with confirm_recording_reset: true only after the user accepts that the affected recordings will be removed and those rooms reset.

Authentication and authorization

Use a TokenProvider to forward the signed-in user's Bearer token, or enable withCredentials for secure HttpOnly cookie sessions. Permission requirements copied from service route guards are included in each operation's JSDoc. The SDK does not embed API keys, credentials, localhost URLs, sandbox hosts, or production hosts.

Inputs, queries, responses, and transport

All methods return the complete Axios response, including status, headers, and request IDs. Request bodies and query objects are named exported interfaces. Nested pagination uses keys such as page[number] and page[size] where required by the service. Standard Axios request options, headers, timeouts, adapters, interceptors, and AbortSignal cancellation are supported as the final argument.

Generic REST resources are capability-guarded. Calling a legacy method that the service does not register throws UnsupportedOperationError before sending a request instead of producing a backend 405.

Errors and cancellation

try {
  await api.client.get("/health", undefined, { signal: AbortSignal.timeout(5_000) });
} catch (error) {
  // Axios errors retain response.status and the service error body.
}

Use @faiber/faiber-ts-sdk when one application needs multiple Faiber services with one configuration.

Versioned interactive learning

api.interactive exposes published previews, authored definition versions, start/resume, draft saves, progressive hints, deterministic submissions and bounded teacher context. The deployment must include the interactive-learning migration and executor. The previous unmounted interactiveContent resource now fails locally; use the supported versioned API.

const catalog = await api.interactive.catalog();
const started = await api.interactive.start(sessionId, { enrollment_id: enrollmentId });
const run = started.data.data;
const result = await api.interactive.submit(run.state.id, {
  revision: run.state.revision,
  activity_id: run.definition.activities[run.state.current_activity].id,
  code: "score = 10",
  idempotency_key: crypto.randomUUID(),
});

The server owns checks and mastery. Browser output must not be submitted as a passing result. Keep the idempotency key for retries of an identical submission; refetch on revision conflict. Definition authoring requires lms:course:read/lms:course:update; learner mutations verify active enrollment ownership and prerequisites. No API credentials belong in browser storage.

Question, option, homework, and course images

Use the same authenticated upload endpoint for all LMS images. File (browser) and Blob (browser or Node 18+) are supported; accepted formats are PNG, JPEG, WebP, and GIF up to 10 MiB.

const upload = await lms.media.uploadImage(file, {
  fileName: 'homework.webp',
  signal: abortController.signal,
  onUploadProgress: ({ loaded, total }) => {
    if (total) updateUploadPercent(Math.round(loaded * 100 / total));
  },
});
const imageUrl = upload.data.data?.url;
if (!imageUrl) throw new Error("Upload did not return an image URL");

The helper sends the multipart image field and inherits the SDK's IDP credentials and refresh flow. Do not set a multipart boundary yourself. Native Axios progress reports bytes sent; await the response before marking the upload successful, because storage can still fail. A canceled or failed upload rejects with AxiosError. Progress availability depends on the Axios adapter/runtime; browser XHR and Node HTTP support byte progress.

Uploading does not automatically change an exercise. Save the returned URL in the question's media array, an option's image_url/media field, the homework item's media array, or the course's image field using its create/update operation. Preserve the other media entries and option values when editing. Returned image URLs are service-relative, so resolve them against your configured LMS service origin for display. This endpoint currently requires lms:course:update, including when the image will be used by an exam or homework.

Course creation and updates accept independent in_person_price and online_price decimal strings (for example, "1200.50" and "800.25"). Course list and detail responses include both fields. On update, omission preserves the saved price and null clears only that price. The existing price field remains available for existing consumers; delivery prices do not overwrite it.

Makeup sessions for selected students

Pass student_user_ids when creating a classroom session to select a group of enrolled students. This roster controls attendance, student session visibility, and room provisioning; the teacher and classroom support user remain room members. Omit the field (or pass null) for the whole classroom. An empty array or a student outside the classroom returns HTTP 422; duplicate IDs are deduplicated. Selection is set at creation.

await lms.classroomSessions.create({
  classroom_id: classroomId,
  course_session_type_id: makeupSessionTypeId,
  name: "Makeup lesson",
  student_user_ids: [firstStudentId, secondStudentId],
  starts_at: "2026-10-25T10:00:00+03:30",
  duration_minutes: 60,
  status: "active",
});

Requires lms:classroom:create and uses configured authentication and Axios request options. The response includes student_user_ids (null for whole-class sessions).

Teacher and support filters

lms.homeworkBanks.list, lms.homeworks.list, lms.listHomeworks, lms.exams.list, lms.examBanks.list, and lms.listExams accept teacher_user_id and support_user_id. These definition filters match the explicitly assigned staff on delivered homework items or exam sessions. Supplying both requires the same delivery to match both staff members. Results retain normal permissions and pagination; the filters do not grant access to other classrooms. Assignments and exam sessions expose the same filters through homeworkAssignments.list and listExamSessions.

const exams = await lms.exams.list({ teacher_user_id: teacherId, support_user_id: supportId, page_size: 20 });
const homeworks = await lms.homeworks.list({ teacher_user_id: teacherId, page_size: 20 });

Uses configured authentication (lms:exam:read / lms:homework:read or corresponding own scopes). Responses retain Axios metadata; permission, validation, cancellation and network failures reject normally. Definition filters require the matching LMS service update.

Student notification settings and assigned chat contacts (0.24.1)

lms.notificationSettings.list() lists sandbox-wide student rules; editing requires lms:notification:settings:update, while reads and read-only previews require lms:notification:settings:read. Update with {enabled,offset_seconds,expected_version}. Event delays are seconds; before/after domain timing is fixed by each rule. Invalid ordering/ranges return 422, stale versions return 409. Changes recompute pending work without replaying delivered occurrences or backfilling old events. preview(key) sends nothing.

lms.eligibleContacts.list({cursor,limit}) returns only current assigned student/teacher/support profile IDs, requiring lms:chat:contacts:read. Resolve authorized profile display information separately. Chat also checks the relationship server-side; hiding contacts in a client is not authorization. All operations use the configured sandbox transport, keep full Axios responses and support cancellation and standard errors.

Summary API migration in 1.0.0

LMS AI summary settings, generation, queue and result methods have been removed. Use ProfileApi.summaries from @faiber/faiber-profile, or sdk.profile.summaries from @faiber/faiber-ts-sdk. Role UUIDs replace the old student/teacher/support enum. The new Profile summary read permissions must be explicitly granted; parent status gives no access. Ordinary LMS student reports, attendance, assessments, feedback and evaluations remain available. Legacy summary data is not imported and there is no compatibility proxy.

Classroom student counts

const response = await api.listClassrooms({ "page[number]": 1, "page[size]": 20 });
for (const classroom of response.data.data.data) {
  console.log(classroom.name, classroom.student_count, classroom.capacity);
}

student_count is a number on classroom list, detail, create and update responses, including api.operations.classroomIndexClassroomGet, api.classrooms.list, and faiber.lms.listClassrooms through @faiber/faiber-ts-sdk. It counts distinct user IDs with an active, inactive, disabled, or enrolled classroom membership. Legacy activation flags do not revoke membership; IDP controls account activation. Removed, withdrawn-after-completion and pending records do not count. Staff assignments and session attendance do not add students. Duplicate enrollment rows count once.

Counts cover the whole classroom, regardless of enrollment pagination, and are batched for the authorized classroom page. Empty classrooms return 0. capacity: null means no limit is configured; a numeric zero remains zero. A UI may display 18 / 25 or a localized 0 / Unlimited. This does not change enrollment or capacity rules. The list requires lms:classroom:read or lms:classroom:read_own with existing record scoping. Normal Axios response, error, authentication and cancellation behavior applies.

Academy completion and exam submission

Academy category, course, session, question and enrollment identifiers are UUID strings. Session completion requires the authenticated enrollment owner and an active enrollment; exam and interactive sessions cannot be completed through the ordinary completion endpoint. Incomplete prerequisite sessions return a conflict. Retain the original idempotency_key when reconciling a session completion.

Exam submission sends answers keyed by question UUID. The server grades the attempt's selected question snapshot; the SDK cannot supply a score or bypass ownership, expiry or attempt status. Submission is not an automatic retry workflow: a response lost after completion requires authoritative readback. Responses retain the full Axios envelope and errors; an empty extracted permission annotation does not mean anonymous access.

Supported wire behavior

The supported backend source is 1a55795725ac5ac23b763117fbbe64f96d4cf2f6. Course/category deletes are available; successful mutation envelopes contain data: {}. Removing a classroom user returns data: null. The legacy videoSections, events, textTemplates, and interactiveContent generic resources are unmounted and reject locally.

Image download operations return raw bytes (ArrayBuffer | Uint8Array) with Axios headers; certificate SVG returns text. Exam-session patches distinguish omission from null: nullable classroom links, staff, timestamps, and counts can be cleared explicitly. The course report returns aggregate classroom/session/attendance counts, while the academy catalog returns learning content and enrollment progress.

Configure Bearer tokens through TokenProvider or secure cookies through withCredentials; prefer in-memory tokens or HttpOnly cookies. Methods use the shared authenticated transport, accept signal, timeout, headers, adapters and interceptors, and preserve full Axios responses. Cancellation and non-success HTTP responses reject with Axios errors. LMS application errors generally serialize { error: string }; educational transfer has its documented structured error envelope.

Permission names in operation documentation describe the backend IDP route metadata. The pinned backend extracts gateway identity headers (with its configured development fallback); that extractor and permission metadata alone do not validate a Bearer token or prove gateway enforcement. Additional handler scope checks apply to scoped reads, learning ownership, evaluation submissions, and sensitive enrollment updates. Native acceptance and deployment verification remain separate from the source contract.

const controller = new AbortController();
const matches = await lms.searchClassrooms({
  search: "GDCN7L", // also accepts classroom name, course name, or existing code
  "page[number]": 1,
  "page[size]": 20,
}, { signal: controller.signal });
for (const classroom of matches.data.data.data) {
  console.log(classroom.short_id, classroom.id, classroom.name);
}

Use your configured LmsApi instance and an AbortController. The existing listClassrooms, classrooms.list, and generated list operation accept the same search parameter. Requires lms:classroom:read or scoped lms:classroom:read_own; all status, relationship, course and pagination filters remain available. Full Axios responses and errors are preserved.

short_id is derived from the UUID without a stored column. Matching is case insensitive and accepts Persian/Arabic digits; enter all six characters for a short-ID lookup. Collisions return multiple results: select using the record's name/course and keep its full UUID for subsequent operations. shortIdFromUuid is also exported for offline display. Requires the service's short-ID migration.

Complete weekly calendar and today view

Available since SDK 1.2.0. Requires an LMS deployment that includes GET /api/v1/classrooms/sessions/calendar.

lms.classroomCalendar loads timetable rows, cards, staff/classroom/course details, student counts, filter badges and summary metrics with one API request. It requires lms:classroom:read, with existing role/own-record visibility restrictions. No report or Profile permissions or frontend enrichment requests are needed. Existing paginated session methods remain compatible.

import { FaiberSDK } from '@faiber/faiber-ts-sdk';
import type { ClassroomCalendarRequest } from '@faiber/faiber-lms';

// Use your existing authenticated SDK instance; secure cookie auth is shown here.
const sdk = new FaiberSDK({
  domains: { lms: 'https://lms.example.com' },
  axios: { withCredentials: true },
});
const controller = new AbortController();

// /dashboard/lms/calendar: one request for this complete local week.
const week: ClassroomCalendarRequest = {
  from: '2026-10-25T00:00:00+03:30',
  to: '2026-11-01T00:00:00+03:30', // exclusive
  timezone: 'Asia/Tehran',
  teacher_ids: ['0451390a-bfab-4de5-b463-b93d6d15b1c5'],
  session_types: ['online', 'interactive'],
};
const weeklyResponse = await sdk.lms.classroomCalendar(week, {
  signal: controller.signal,
});
const weekly = weeklyResponse.data.data;
// Render timetable/cards from weekly.sessions, metrics from weekly.summary,
// and stable type-filter badges from weekly.type_counts. No enrichment calls.

// /dashboard/lms/today: one request for the selected local date.
// This is an alternative page load, not an additional request for the weekly page.
const dailyResponse = await sdk.lms.classroomCalendar({
  date: '2026-10-25',
  timezone: 'Asia/Tehran',
}, { signal: controller.signal });
const daily = dailyResponse.data.data;
for (const session of daily.sessions) {
  const time = new Intl.DateTimeFormat('fa-IR', {
    timeZone: daily.range.timezone, hour: '2-digit', minute: '2-digit',
  }).format(new Date(session.starts_at));
  console.log(session.title, time, session.student_count); // zero stays zero
}

The individual LmsApi exposes the same classroomCalendar method. The exact wire operation is lms.operations.classroomCalendarGet, with CSV strings instead of arrays. All entry points preserve the complete Axios response, errors, headers, request options, token/cookie authentication and cancellation. Selector opening and explicit calendar actions may make their own requests.

Calendar contract

  • Supply required IANA timezone and either date (YYYY-MM-DD) or from/to (RFC3339 instants). Ranges cover 1–7 complete local days, inclusive opening midnight and exclusive closing midnight. DST days may have 23/25 hours; weeks may have 167/169 hours. Ambiguous/nonexistent midnight is rejected explicitly.
  • Sessions are included by their start instant, sorted by starts_at then id. There is no pagination, result cap or silent truncation. Undated supplementary sessions cannot be assigned to a day and are excluded from rows and metrics. Dated supplementary sessions are included; cancelled/completed sessions remain visible. Soft-deleted sessions/classrooms are excluded.
  • Array filters support multiple teacher_ids, session_types (categories), and session_type_ids (configured type UUIDs), plus one classroom_id. Each list uses OR, separate filters use AND. Empty convenience arrays mean all values; wire CSV filters must have 1–100 entries. Unknown values are rejected, not ignored.
  • Categories: online (آنلاین), in_person (حضوری), makeup (جبرانی), interactive (آنلاین تعاملی), supplementary (مکمل), plus unknown for unmapped configured types. delivery_mode uses the same categories. Lifecycle, record status and access mode are separate fields.
  • summary describes returned rows after all filters. type_counts, configured_type_counts, and total_before_type_filters respect visibility, date, teacher and classroom filters but ignore both type filters. Category counts always include all categories with genuine zero values.
  • Effective teacher is session teacher with classroom fallback; support is classroom support with consultant fallback. Null IDs mean unassigned; a non-null ID with null profile means no Profile record was returned. Display names are strings, locale maps, or null. A Profile service failure rejects the whole request with 502 rather than silently omitting enrichment.
  • student_count: distinct selected IDs when restricted (empty selection = 0), otherwise distinct active/inactive/disabled/enrolled classroom members. classroom_student_count always means enrollment, and classroom_capacity is independent: null is unconfigured and zero is preserved. Summary student_seat_count sums session counts, not unique people across the range.
  • Missing duration/end time/type/room/lifecycle values remain null. The sum of known durations is paired with unknown_duration_count. Missing lifecycle is grouped under unknown. Timing/access fields come from LMS; do not manually add timezone offsets to timestamps or derive access mode from category.
  • Query shape/unknown keys (including pagination) return 400; range/timezone/filter validation returns 422; missing permission returns 403 (gateway auth may return 401); Profile failures return 502. Catch Axios errors and keep cancellation distinct from failed loading. There is no SDK retry or enrichment fallback.

Public types include ClassroomCalendarRequest, ClassroomCalendarQuery, ClassroomCalendarResponse, ClassroomCalendarData, ClassroomCalendarSession, CalendarSummary, CalendarProfileSummary, CalendarSessionType, CalendarSessionCategory, and explicit range/filter/count types. Package release and compatible backend deployment are both required before enabling this method in a production client.

Confirm classroom course or schedule changes

lms.operations.classroomUpdateClassroomPatch(id, input, options) and lms.classrooms.update(id, input, options) accept the same typed confirmation flags. The caller needs lms:classroom:update. Omit both flags on the initial request. HTTP 409 with response.data.error equal to classroom_session_changes_confirmation_required means that replacing the course or rescheduling started sessions needs consent. A separate recording_reset_confirmation_required conflict protects existing room recordings. The SDK preserves the Axios error and does not retry or confirm automatically.

Before asking for confirmation, explain that course-linked sessions are mapped to the destination course in order. A shorter destination archives surplus sessions; a longer destination creates additional sessions. Standalone meetings are retained. Affected room recordings are deleted and measured online attendance is reset; manual attendance and archived session history are retained. Schedule changes can affect sessions that have already started. On cancel, send no further request.

import type { LmsService } from '@faiber/faiber-ts-sdk';

const proposed: LmsService.ClassroomUpdateClassroomPatchInput = {
  course_id: destinationCourseId,
  starts_at: '2026-11-07T13:00:00Z',
};
// Initial save; handle HTTP 409 in your application's confirmation UI.
await sdk.lms.operations.classroomUpdateClassroomPatch(classroomId, proposed);

// Run this separately, only after the user accepts all consequences above.
const confirmed: LmsService.UpdateClassroomInput = {
  ...proposed,
  confirm_session_changes: true,
  confirm_recording_reset: true,
};
await sdk.lms.classrooms.update(classroomId, confirmed, { signal: controller.signal });

Keep the pending payload immutable while confirmation is open. If the user edits it, discard consent and submit a new unconfirmed request. The server applies the change transactionally and rejects invalid course/session links without partial updates. These flags require the LMS service revision supporting classroom course replacement; publishing or upgrading the SDK alone does not deploy that service.

Certificate filters and session media

These capabilities require LMS revision c24b8e68a051d9330ff8f3360bf6677ddb723a2d or later. Publishing the SDK does not deploy the LMS backend.

const certificates = await sdk.lms.certificates.list({
  user_id: studentUserId,
  profile_search: "علی", // localized name, email, phone, national code, short ID or user ID
  status: "issued",
  certificate_template_id: templateId, // certificate type; certificate_type is an alias
  course_id: courseId,
  issued_from: "2026-01-01T00:00:00+03:30",
  issued_to: "2026-12-31T23:59:59+03:30",
  "page[number]": 1,
  "page[size]": 20,
});
const sessions = await sdk.lms.classroomSessions.list({ classroom_id: classroomId });
for (const row of sessions.data.data.data) {
  const links = sdk.lms.classroomSessionMediaLinks(row);
  // links.recording opens the Session archive; links.recordingsApi lists ready recordings.
  // links.playback opens scheduled Session playback; links.compositionManifest targets DRM.
  if (row.media.drm_mixed_media_id) {
    const preview = await sdk.drm.classroomSessionComposition(row);
    // Use preview.data.document in a composition player; preview.data.version identifies it.
  }
}

These examples use the complete SDK (sdk); standalone LmsApi exposes the same LMS methods. Certificate filters combine with AND before count/pagination. student_user_id aliases user_id. Additional filters: classroom_id, grade_id, issued_by, public_id, verification_code, legacy_code, search (literal title/code/public-ID search), has_media, has_grade, revoked, and inclusive created_from/to, updated_from/to, revoked_from/to timestamps. Use RFC3339 with a timezone for all date bounds. Do not send both a canonical field and its alias. A certificate type is an existing certificate template, not a separate new taxonomy. lms:certificate:read_own remains restricted to the caller's issued certificates; full reads require lms:certificate:read. Invalid ranges return 422; profile search failures return 502.

Every classroom session list/detail response includes media with nullable relative paths, and course_session.drm_mixed_media_id. Paths are discovery metadata, not access grants or signed URLs. Session paths use the Session domain; DRM paths use the DRM domain. Recordings require Session recording permission and may be empty until processing finishes. Composition preview requires drm:composition:read and works for every session type, including locked, future and roomless sessions when a composition is linked. An unpublished/missing manifest returns 404. Playback and license operations keep their own DRM permissions. Browser links need an authenticated destination session; SDK requests use your configured credentials.

examDelivery(attemptId) now returns correct answers, option answer metadata and per-question grading to authorized administrators and assigned reviewers while an attempt is active. Students still receive redacted keys until completion/expiry/cancellation. This uses the existing lms:exam:read or scoped lms:exam:read_own checks and assignment relationships, never a role name or a client-supplied reviewer flag. passed remains final-result-only.

Private lobby displays

Use lms.operations.displaySnapshotGet() for the server-configured current-day display. It requires lms:display:read, returns raw snapshot JSON (no envelope), and excludes student identities and room credentials. Authorize the returned private channel with displayAuthorizePost({ socket_id, channel_name }). The password or access token must never be embedded in a page URL or browser bundle.

Subscribe to snapshot.socket.event, fetch a snapshot once after subscription, then apply LmsDisplayPatch parts atomically against base_version. Ignore already-applied versions. Recover from incomplete parts, version gaps, and reconnects with a fresh snapshot; do not poll. The server fixes timezone/day and returns 503 if the display is disabled or not ready. Student count changes and session lifecycle updates are pushed after committed LMS changes. Staff names are refreshed with the next LMS projection refresh or day rollover.

版本与发布记录

@faiber/faiber-lms 共发布 66 个版本,首次发布于 2026/07/18;过去 12 个月发布了 66 个版本。

版本发布日期距今
2.8.0latest2026/11/14今天
2.7.02026/11/059 天前
2.6.02026/11/049 天前
2.5.02026/11/049 天前
2.4.02026/11/0310 天前
2.3.02026/11/0211 天前

关于 @faiber/faiber-lms 的常见问题

@faiber/faiber-lms 是什么?

Educational data transfer

如何安装 @faiber/faiber-lms?

在项目目录运行 npm install @faiber/faiber-lms,或使用 pnpm add / yarn add / bun add。

@faiber/faiber-lms 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@faiber/faiber-lms 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@faiber/faiber-lms 可以商用吗?

@faiber/faiber-lms 使用 未声明 许可。package.json 没有声明许可证。没有许可证的代码默认保留全部权利,商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括,不构成法律意见,请以许可原文为准。

@faiber/faiber-lms 的下载量有多少?

根据 npm 官方下载统计,@faiber/faiber-lms 在 2026/10/14 – 2026/11/12 这 30 天内被下载 3,761 次,日均约 125 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@faiber/faiber-lms 还在维护吗?

最新版本 2.8.0 发布于 2026/11/14(今天);至今共发布 66 个版本,其中过去 12 个月发布了 66 个;首次发布于 2026/07/18。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。