{"_id":"@beanchain/handbook-lms","_rev":"2-7f5233611f16e6f8c3f76e15ec105996","name":"@beanchain/handbook-lms","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@beanchain/handbook-lms","version":"0.1.0","_id":"@beanchain/handbook-lms@0.1.0","maintainers":[{"name":"dbaxterbchain","email":"david.baxter@bchain.coffee"}],"dist":{"shasum":"8163a6f431509ca8c6a7892c437e385962a679f2","tarball":"https://registry.npmjs.org/@beanchain/handbook-lms/-/handbook-lms-0.1.0.tgz","fileCount":69,"integrity":"sha512-S3qwsDU+nJ4gPpU3+8jVaU5O+i0wCzMdBh8nVoHP5NADBOWhPqGFNYR6rGtvo34/oRKTaswFV70WDywjNxhfAA==","signatures":[{"sig":"MEYCIQDzrVqnzWvYq5r+/Ux39JhJqRppukgOmCK1NDzitooYpQIhAOJUh5CiVz1bt2YBBPx2fnR7C9yHBUYnaxTExYRwHiOA","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1826795},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./sql":"./sql/README.md","./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js","require":"./dist/core/index.cjs"},"./react":{"types":"./dist/react/index.d.ts","import":"./dist/react/index.js","require":"./dist/react/index.cjs"},"./config":{"types":"./dist/config/index.d.ts","import":"./dist/config/index.js","require":"./dist/config/index.cjs"},"./adapter":{"types":"./dist/adapter/index.d.ts","import":"./dist/adapter/index.js","require":"./dist/adapter/index.cjs"},"./contracts":{"types":"./dist/contracts/index.d.ts","import":"./dist/contracts/index.js","require":"./dist/contracts/index.cjs"},"./react-mui":{"types":"./dist/react-mui/index.d.ts","import":"./dist/react-mui/index.js","require":"./dist/react-mui/index.cjs"},"./react-headless":{"types":"./dist/react-headless/index.d.ts","import":"./dist/react-headless/index.js","require":"./dist/react-headless/index.cjs"}},"gitHead":"577fb207aabada00a642643a3a39ce3b574bd67d","private":false,"scripts":{"build":"tsup --config tsup.config.ts","clean":"rimraf dist","typecheck":"tsc -p tsconfig.json --noEmit","build:watch":"tsup --config tsup.config.ts --watch","prepublishOnly":"npm run build"},"_npmUser":{"name":"dbaxterbchain","email":"david.baxter@bchain.coffee"},"_npmVersion":"11.9.0","description":"Reusable LMS core contracts and React primitives for Handbook courses.","directories":{},"sideEffects":false,"_nodeVersion":"22.21.1","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","rimraf":"^6.0.1","typescript":"^5.6.3","@types/react":"^18.3.12"},"peerDependencies":{"zod":"^3.23.0","react":"^18.0.0 || ^19.0.0","@mui/material":"^7.0.0","@hello-pangea/dnd":"^18.0.0","@mui/icons-material":"^7.0.0","@uiw/react-md-editor":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/handbook-lms_0.1.0_1771978942549_0.09182610999111351","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@beanchain/handbook-lms","version":"0.2.0","description":"Reusable LMS core contracts and React primitives for Handbook courses.","type":"module","private":false,"sideEffects":false,"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js","require":"./dist/core/index.cjs"},"./react":{"types":"./dist/react/index.d.ts","import":"./dist/react/index.js","require":"./dist/react/index.cjs"},"./contracts":{"types":"./dist/contracts/index.d.ts","import":"./dist/contracts/index.js","require":"./dist/contracts/index.cjs"},"./config":{"types":"./dist/config/index.d.ts","import":"./dist/config/index.js","require":"./dist/config/index.cjs"},"./adapter":{"types":"./dist/adapter/index.d.ts","import":"./dist/adapter/index.js","require":"./dist/adapter/index.cjs"},"./react-headless":{"types":"./dist/react-headless/index.d.ts","import":"./dist/react-headless/index.js","require":"./dist/react-headless/index.cjs"},"./react-mui":{"types":"./dist/react-mui/index.d.ts","import":"./dist/react-mui/index.js","require":"./dist/react-mui/index.cjs"},"./sql":"./sql/README.md"},"scripts":{"build":"tsup --config tsup.config.ts","build:watch":"tsup --config tsup.config.ts --watch","typecheck":"tsc -p tsconfig.json --noEmit","clean":"rimraf dist","prepublishOnly":"npm run build"},"peerDependencies":{"@hello-pangea/dnd":"^18.0.0","@mui/icons-material":"^7.0.0","@mui/material":"^7.0.0","@uiw/react-md-editor":"^4.0.0","react":"^18.0.0 || ^19.0.0","zod":"^3.23.0"},"devDependencies":{"@types/react":"^18.3.12","rimraf":"^6.0.1","tsup":"^8.5.0","typescript":"^5.6.3"},"publishConfig":{"registry":"https://registry.npmjs.org","access":"public"},"gitHead":"3e1b6edaca8cd542e2743ae7fb89555f728fca5d","_id":"@beanchain/handbook-lms@0.2.0","_nodeVersion":"24.13.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-WCpx4A4aKXJceRoiuSRXPpvfVhjpI8FsiqtrR3/Am9kJNqFmzsX3lRbUtdwgryP86XLzeGByZDEo11ouMW4MBQ==","shasum":"3f8d0d3901d156446125a286a149c09b5be14bfa","tarball":"https://registry.npmjs.org/@beanchain/handbook-lms/-/handbook-lms-0.2.0.tgz","fileCount":69,"unpackedSize":2255149,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCID3g9xHpo8RqpsGoNCievWKofE7fykvs8UAMp9z2ohpjAiEA3FgHxm7uaSwQhRvRA/dXoS6XrKnTWr9XLQVEy8lNRSc="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:50b3b2a7-5822-4364-8611-682888bf9293"}},"directories":{},"maintainers":[{"name":"dbaxterbchain","email":"david.baxter@bchain.coffee"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/handbook-lms_0.2.0_1772002788034_0.6650737022511493"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-25T00:22:22.442Z","modified":"2026-02-25T06:59:48.393Z","0.1.0":"2026-02-25T00:22:22.742Z","0.2.0":"2026-02-25T06:59:48.242Z"},"description":"Reusable LMS core contracts and React primitives for Handbook courses.","maintainers":[{"name":"dbaxterbchain","email":"david.baxter@bchain.coffee"}],"readme":"# @beanchain/handbook-lms\n\nReusable package for Handbook LMS logic.\n\n## Modules\n\n- `@beanchain/handbook-lms/core`: Canonical LMS types, schemas, and shared contracts.\n- `@beanchain/handbook-lms/adapter`: Adapter interfaces for host data integration.\n- `@beanchain/handbook-lms/config`: Strict UI config contract and `defineHandbookLmsConfig` helper.\n- `@beanchain/handbook-lms/react-headless`: Headless editor/review/learning hooks.\n- `@beanchain/handbook-lms/react-mui`: Prebuilt MUI editor/review/learning components.\n- `@beanchain/handbook-lms/react`: Legacy provider + headless course library component.\n- `@beanchain/handbook-lms/contracts`: HTTP endpoint constants and helpers.\n- `@beanchain/handbook-lms/sql`: Provider-agnostic DB guidance + Supabase appendix.\n\n## Canonical contracts\n\nUse `@beanchain/handbook-lms/contracts` for canonical runtime schemas:\n\n- Entity schemas: course, section/module/stage/question hierarchy, progress, assignments, learning summary.\n- Mutation schemas: curriculum CRUD payloads, reorder payloads, review payloads, assignment update payloads.\n\nThese schemas are intended for adapter-boundary validation only. Package hooks/components assume canonical data and should not receive domain-specific field names.\n\n## Adapter boundary pattern\n\nValidate requests/responses in host adapters before crossing the package boundary:\n\n```ts\nimport {\n  handbookLmsSectionListSchema,\n  handbookLmsReviewModuleInputSchema\n} from \"@beanchain/handbook-lms/contracts\";\n\nconst curriculum = handbookLmsSectionListSchema.parse(await apiFetch(\"/api/lms-courses/.../curriculum\"));\nconst reviewPayload = handbookLmsReviewModuleInputSchema.parse(input);\n```\n\nIf your backend uses different naming, map to canonical package fields in the adapter (for example `target_exercise_ids -> target_question_ids`).\n\n## Host app setup checklist\n\n- Implement `HandbookLmsAdapter` per domain (`team courses`, `solution seeking`, etc.).\n- Define strict UI config with `defineHandbookLmsConfig`.\n- Keep backend endpoints + database migrations in host repo.\n- Pass only canonical models into package hooks/components.\n- Optionally pass `notifications` callbacks into package UI components to integrate host-level toast systems.\n\n## Draft publishing workflow\n\nThe package assumes a true draft model for course editing:\n\n- Draft metadata + curriculum are edited privately through draft endpoints.\n- Live course `published` and `active` toggles stay separate and remain immediate controls.\n- Publishing applies draft state to live in one operation.\n- Discarding removes draft state without changing live content.\n- Adapter methods required for this workflow:\n  - `getDraft(courseSlug)`\n  - `lockDraft(courseSlug)`\n  - `saveDraftCourseDetails(courseSlug, data)`\n  - `publishDraft(courseSlug)`\n  - `discardDraft(courseSlug)`\n  - `updateLiveCourseSettings({ courseId, data: { published?, active? } })`\n\nRecommended host endpoint contract:\n\n- `GET /api/lms-courses-admin/:slug/draft`\n- `PATCH /api/lms-courses-admin/:slug/draft/course`\n- `POST /api/lms-courses-admin/:slug/draft/lock`\n- `POST /api/lms-courses-admin/:slug/draft/publish`\n- `POST /api/lms-courses-admin/:slug/draft/discard`\n\nFor prebuilt editor UI, use `HandbookLmsCourseEditorWorkspace` or compose these primitives:\n\n- `HandbookLmsDraftDetailsCard`\n- `HandbookLmsLiveAvailabilityCard`\n- `HandbookLmsEditorControlsCard`\n- `HandbookLmsDraftChangesDialog`\n- `HandbookLmsCurriculumEditor`\n\n`HandbookLmsCurriculumEditor` includes:\n\n- Distinct section/module/stage/question visual hierarchy styling for easier scanning.\n- Collapsible module bodies with explicit expand/collapse controls.\n- Auto-expansion when adding or saving stages.\n\nExample compositions:\n\n- Team full editor: show all cards + curriculum structure.\n- Single-course admin editor: hide draft details/live availability, keep editor controls + structure.\n\n## UI config reference\n\nAll keys in `HandbookLmsUiConfig` are required and strongly typed.\n\nAutosave notification keys:\n\n- `labels.autosaveNotice`: Message used for autosave success notices.\n- `workflow.enableAutosaveNotifications`: Enables/disables autosave notices from learning workspace.\n- `workflow.autosaveNotificationCooldownMs`: Minimum interval between autosave notices.\n\nRecommended defaults:\n\n- `workflow.enableAutosaveNotifications: true`\n- `workflow.autosaveNotificationCooldownMs: 12000`\n- `labels.autosaveNotice: \"Progress autosaved.\"`\n\nExample:\n\n```ts\nimport { defineHandbookLmsConfig } from \"@beanchain/handbook-lms/config\";\n\nexport const lmsConfig = defineHandbookLmsConfig({\n  labels: {\n    editorTitle: \"Course Editor\",\n    reviewTitle: \"Course Review\",\n    backButton: \"Back\",\n    addSection: \"Add section\",\n    addModule: \"Add module\",\n    addStage: \"Add stage\",\n    addQuestion: \"Add question\",\n    editAction: \"Edit\",\n    archiveAction: \"Archive\",\n    restoreAction: \"Restore\",\n    saveAction: \"Save\",\n    cancelAction: \"Cancel\",\n    saveResponseAction: \"Save response\",\n    submitModuleAction: \"Submit module\",\n    passAction: \"Mark passed\",\n    requestRevisionAction: \"Request revision\",\n    applyStatusAction: \"Apply status\",\n    noModulesMessage: \"No modules are currently configured.\",\n    autosaveNotice: \"Progress autosaved.\"\n  },\n  workflow: {\n    enableModuleDragAndDrop: true,\n    enableStatusOverride: true,\n    enableFollowUpAssignments: true,\n    enableTargetedQuestionSelection: true,\n    enableAutosaveNotifications: true,\n    autosaveNotificationCooldownMs: 12000\n  },\n  validation: {\n    slugPattern: /^[a-z0-9]+(?:-[a-z0-9]+)*$/,\n    slugPatternMessage: \"Use lowercase letters, numbers, and single dashes only.\",\n    requiredTitleMessage: \"Title is required.\",\n    requiredSlugMessage: \"Slug is required.\",\n    requiredSummaryMessage: \"Summary is required.\",\n    requiredPromptMessage: \"Prompt is required.\"\n  },\n  naming: {\n    course: \"Course\",\n    section: \"Section\",\n    module: \"Module\",\n    stage: \"Stage\",\n    question: \"Question\",\n    learner: \"Learner\",\n    reviewer: \"Reviewer\",\n    assignment: \"Assignment\"\n  }\n});\n```\n\nNotes:\n\n- Autosave notices are emitted through `notifications` callbacks passed to `HandbookLmsLearningWorkspace`.\n- If no external notification callbacks are provided, autosave notices are suppressed by default.\n- Manual save/submit notifications still work through the existing notification pathways.\n\n## Semver policy\n\n- `MAJOR`: Breaking API or schema contract changes.\n- `MINOR`: Backward-compatible API additions.\n- `PATCH`: Bug fixes and documentation-only updates.\n\n## Publishing\n\nThis package is published to npm using the workflow at `.github/workflows/publish-handbook-lms.yml`.\n\n## Local development\n\n- Build compiled outputs: `npm run build --workspace @beanchain/handbook-lms`\n- Typecheck package only: `npm run typecheck --workspace @beanchain/handbook-lms`\n\n## Peer dependencies\n\n- `react`\n- `@mui/material`\n- `@mui/icons-material`\n- `@hello-pangea/dnd`\n- `@uiw/react-md-editor`\n- `zod`\n","readmeFilename":"README.md"}