@tinyweb_dev/oe-exam-sdk 0.1.20 → 0.1.24

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.
Files changed (87) hide show
  1. package/README.md +4 -5
  2. package/dist/api/result-review.types.d.ts +1 -0
  3. package/dist/components/exams/ExamPreviewDialog.d.ts +6 -7
  4. package/dist/components/exams/ExamPreviewDialog.js +1 -1
  5. package/dist/components/exams/ExamPreviewPartContent.d.ts +18 -0
  6. package/dist/components/exams/ExamPreviewPartContent.js +1 -0
  7. package/dist/components/exams/ExamPreviewSidebar.d.ts +17 -0
  8. package/dist/components/exams/ExamPreviewSidebar.js +1 -0
  9. package/dist/components/exams/exam-preview.types.d.ts +34 -0
  10. package/dist/components/exams/exam-preview.types.js +1 -0
  11. package/dist/components/exams/index.d.ts +1 -0
  12. package/dist/components/exams/take/components/question-renderers/MoversChooseBestAnswerRenderer.js +1 -1
  13. package/dist/components/exams/take/components/question-renderers/MoversListenAndWriteRenderer.js +1 -1
  14. package/dist/components/exams/take/components/question-renderers/MoversWordFillStructuredFormRenderer.js +1 -1
  15. package/dist/components/exams/take/components/question-renderers/MoversWritingRenderer.js +1 -1
  16. package/dist/components/exams/take/exam-taking.utils.d.ts +13 -0
  17. package/dist/components/exams/take/exam-taking.utils.js +1 -1
  18. package/dist/components/exams/take/types.d.ts +3 -0
  19. package/dist/components/exams/take/utils/question-transformers.d.ts +16 -1
  20. package/dist/components/exams/take/utils/question-transformers.js +1 -1
  21. package/dist/components/questions/_shared/types/word-fill-structured-form.type.d.ts +25 -0
  22. package/dist/components/questions/_shared/utils/parseFillBlankStem.d.ts +30 -0
  23. package/dist/components/questions/_shared/utils/parseFillBlankStem.js +1 -0
  24. package/dist/components/questions/index.d.ts +1 -0
  25. package/dist/components/questions/index.js +1 -1
  26. package/dist/components/questions/question-bank/EditQuestionBankDialog.d.ts +2 -0
  27. package/dist/components/questions/question-bank/EditQuestionBankDialog.js +1 -0
  28. package/dist/components/questions/question-bank/QuestionEditorRenderer.d.ts +23 -0
  29. package/dist/components/questions/question-bank/QuestionEditorRenderer.js +1 -0
  30. package/dist/components/questions/question-bank/difficulty.d.ts +15 -0
  31. package/dist/components/questions/question-bank/difficulty.js +1 -0
  32. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.d.ts +5 -0
  33. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.js +1 -0
  34. package/dist/components/questions/question-bank/editor-adapters/index.d.ts +5 -0
  35. package/dist/components/questions/question-bank/editor-adapters/index.js +1 -0
  36. package/dist/components/questions/question-bank/editor-adapters/media-adapters.d.ts +6 -0
  37. package/dist/components/questions/question-bank/editor-adapters/media-adapters.js +1 -0
  38. package/dist/components/questions/question-bank/editor-adapters/types.d.ts +1 -0
  39. package/dist/components/questions/question-bank/editor-adapters/types.js +1 -0
  40. package/dist/components/questions/question-bank/editor-adapters/unsupported-fallback.d.ts +8 -0
  41. package/dist/components/questions/question-bank/editor-adapters/unsupported-fallback.js +1 -0
  42. package/dist/components/questions/question-bank/editor-adapters/writing-adapters.d.ts +5 -0
  43. package/dist/components/questions/question-bank/editor-adapters/writing-adapters.js +1 -0
  44. package/dist/components/questions/question-bank/index.d.ts +4 -0
  45. package/dist/components/questions/question-bank/index.js +1 -0
  46. package/dist/components/questions/question-bank/types.d.ts +59 -0
  47. package/dist/components/questions/question-bank/types.js +1 -0
  48. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +1 -1
  49. package/dist/components/questions/types/choose-the-correct-answer/index.d.ts +1 -0
  50. package/dist/components/questions/types/choose-the-correct-answer/index.js +1 -1
  51. package/dist/components/questions/types/choose-the-correct-answer/normalize.d.ts +27 -0
  52. package/dist/components/questions/types/choose-the-correct-answer/normalize.js +1 -0
  53. package/dist/components/questions/types/choose-the-correct-answer/register.js +1 -1
  54. package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +1 -1
  55. package/dist/components/questions/types/fill-in-blank/register.js +1 -1
  56. package/dist/components/questions/types/fill-in-blank/transform.js +1 -1
  57. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.d.ts +1 -1
  58. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +1 -1
  59. package/dist/components/questions/types/word-fill-structured-form/transform.js +1 -1
  60. package/dist/components/questions/viewer/QuestionViewer.js +1 -1
  61. package/dist/components/results/review-data.js +1 -1
  62. package/dist/components/themes/cambridge-yle/CambridgeYleAudioPlayer.d.ts +2 -1
  63. package/dist/components/themes/cambridge-yle/CambridgeYleAudioPlayer.js +1 -1
  64. package/dist/components/themes/english-certification/EcWordFillStructuredForm.js +1 -1
  65. package/dist/index.d.ts +2 -1
  66. package/dist/index.js +1 -1
  67. package/dist/shared/lib/i18n/messages/en/admin.d.ts +11 -4
  68. package/dist/shared/lib/i18n/messages/en/admin.js +1 -1
  69. package/dist/shared/lib/i18n/messages/en.d.ts +11 -4
  70. package/dist/shared/lib/i18n/messages/vi/admin.d.ts +11 -4
  71. package/dist/shared/lib/i18n/messages/vi/admin.js +1 -1
  72. package/dist/shared/lib/i18n/messages/vi.d.ts +11 -4
  73. package/package.json +163 -163
  74. package/dist/mcp/config.d.ts +0 -12
  75. package/dist/mcp/config.js +0 -1
  76. package/dist/mcp/http-client.d.ts +0 -6
  77. package/dist/mcp/http-client.js +0 -1
  78. package/dist/mcp/index.d.ts +0 -6
  79. package/dist/mcp/index.js +0 -2
  80. package/dist/mcp/tools/_helpers.d.ts +0 -4
  81. package/dist/mcp/tools/_helpers.js +0 -1
  82. package/dist/mcp/tools/exams.d.ts +0 -2
  83. package/dist/mcp/tools/exams.js +0 -1
  84. package/dist/mcp/tools/index.d.ts +0 -2
  85. package/dist/mcp/tools/index.js +0 -1
  86. package/dist/mcp/tools/results.d.ts +0 -2
  87. package/dist/mcp/tools/results.js +0 -1
@@ -275,6 +275,12 @@ declare const vi: {
275
275
  readonly 'admin.questionBank.dialog.edit.save': "Lưu thay đổi";
276
276
  readonly 'admin.questionBank.dialog.edit.codeError': "Vui lòng nhập mã câu hỏi";
277
277
  readonly 'admin.questionBank.dialog.edit.titleError': "Vui lòng nhập tiêu đề";
278
+ readonly 'admin.questionBank.dialog.edit.loading': "Đang tải...";
279
+ readonly 'admin.questionBank.dialog.edit.unsupportedNote': "Lưu ý:";
280
+ readonly 'admin.questionBank.dialog.edit.unsupportedType': "Loại câu hỏi \"{type}\" chưa hỗ trợ chỉnh sửa trực tiếp trong giao diện này. Vui lòng sử dụng trang tạo đề thi để chỉnh sửa câu hỏi này.";
281
+ readonly 'admin.questionBank.dialog.edit.contentLabel': "Nội dung câu hỏi";
282
+ readonly 'admin.questionBank.dialog.edit.answerLabel': "Đáp án đúng";
283
+ readonly 'admin.questionBank.dialog.edit.explanationLabel': "Giải thích";
278
284
  readonly 'admin.questionBank.dialog.delete.title': "Xác nhận xóa câu hỏi";
279
285
  readonly 'admin.questionBank.dialog.delete.message': "Bạn có chắc chắn muốn xóa câu hỏi";
280
286
  readonly 'admin.questionBank.dialog.delete.warning': "Hành động này không thể hoàn tác.";
@@ -1544,9 +1550,6 @@ declare const vi: {
1544
1550
  readonly 'admin.exams.shuffle.info3': "Các câu hỏi trong cùng 1 Part được xáo trộn ngẫu nhiên";
1545
1551
  readonly 'admin.exams.shuffle.info4': "Các đề thi con được liên kết với đề thi cha để quản lý";
1546
1552
  readonly 'admin.exams.shuffle.countLabel': "Số lượng đề thi con muốn tạo";
1547
- readonly 'admin.exams.shuffle.strategyLabel': "Lựa chọn xử lý đề thi cũ";
1548
- readonly 'admin.exams.shuffle.keepOld': "Giữ lại các đề thi cũ và thêm đề mới";
1549
- readonly 'admin.exams.shuffle.replaceAll': "Tạo bộ đề mới hoàn toàn";
1550
1553
  readonly 'admin.exams.shuffle.existingVariants': "Đề thi đã có ({count})";
1551
1554
  readonly 'admin.exams.shuffle.deleteAll': "Xóa tất cả ({count})";
1552
1555
  readonly 'admin.exams.shuffle.loading': "Đang tải...";
@@ -1637,21 +1640,25 @@ declare const vi: {
1637
1640
  readonly 'admin.exams.preview.totalPoints': "Tổng điểm:";
1638
1641
  readonly 'admin.exams.preview.progress': "Tiến độ";
1639
1642
  readonly 'admin.exams.preview.loading': "Đang tải...";
1640
- readonly 'admin.exams.preview.questionNavigation': "Điều hướng câu hỏi";
1643
+ readonly 'admin.exams.preview.questionNavigation': "Điều hướng Part";
1641
1644
  readonly 'admin.exams.preview.all': "Tất cả";
1642
1645
  readonly 'admin.exams.preview.allQuestions': "Tất cả {count} câu hỏi";
1643
1646
  readonly 'admin.exams.preview.partQuestions': "Part {part}: {count} câu";
1644
1647
  readonly 'admin.exams.preview.collapse': "Thu gọn";
1645
1648
  readonly 'admin.exams.preview.detail': "Chi tiết";
1646
1649
  readonly 'admin.exams.preview.questionIndex': "Câu hỏi {index} / {total}";
1650
+ readonly 'admin.exams.preview.partIndex': "Part {index} / {total}";
1647
1651
  readonly 'admin.exams.preview.noQuestions': "Chưa có câu hỏi";
1648
1652
  readonly 'admin.exams.preview.loadingQuestions': "Đang tải câu hỏi...";
1649
1653
  readonly 'admin.exams.preview.retry': "Thử lại";
1650
1654
  readonly 'admin.exams.preview.examNoQuestions': "Đề thi này chưa có câu hỏi nào.";
1651
1655
  readonly 'admin.exams.preview.prevQuestion': "Câu trước";
1656
+ readonly 'admin.exams.preview.prevPart': "Part trước";
1652
1657
  readonly 'admin.exams.preview.prev': "Trước";
1653
1658
  readonly 'admin.exams.preview.nextQuestion': "Câu tiếp theo";
1659
+ readonly 'admin.exams.preview.nextPart': "Part tiếp theo";
1654
1660
  readonly 'admin.exams.preview.next': "Tiếp";
1661
+ readonly 'admin.exams.preview.questionLabel': "Câu {number}";
1655
1662
  readonly 'admin.exams.preview.footerNote': "Đây là chế độ xem trước để kiểm tra bố cục và nội dung đề thi.";
1656
1663
  readonly 'admin.exams.preview.close': "Đóng";
1657
1664
  readonly 'admin.exams.preview.loadError': "Không thể tải câu hỏi. Vui lòng thử lại.";
package/package.json CHANGED
@@ -1,163 +1,163 @@
1
- {
2
- "name": "@tinyweb_dev/oe-exam-sdk",
3
- "version": "0.1.20",
4
- "description": "Reusable OceanEdu question components.",
5
- "type": "module",
6
- "main": "./dist/index.js",
7
- "module": "./dist/index.js",
8
- "types": "./dist/index.d.ts",
9
- "bin": {
10
- "oe-exam-mcp": "./dist/mcp/index.js"
11
- },
12
- "files": [
13
- "dist",
14
- "README.md"
15
- ],
16
- "sideEffects": [
17
- "**/*.css"
18
- ],
19
- "scripts": {
20
- "clean": "rm -rf dist",
21
- "build:compile": "tsc -p tsconfig.build.json",
22
- "build:alias": "tsc-alias -p tsconfig.build.json",
23
- "build:ts": "npm run build:compile && npm run build:assets && npm run build:alias",
24
- "build:assets": "for d in cambridge-yle english-certification summer-sky; do [ -d src/components/themes/$d/assets ] && cp -R src/components/themes/$d/assets dist/components/themes/$d/ || true; done; [ -d src/components/shared/audio/assets ] && cp -R src/components/shared/audio/assets dist/components/shared/audio/ || true; [ -d src/components/results/report/assets ] && mkdir -p dist/components/results/report && cp -R src/components/results/report/assets dist/components/results/report/ || true; (cd src && find . -name '*.module.css' -exec cp --parents {} ../dist/ \\;); cp src/global.d.ts dist/global.d.ts",
25
- "minify:js": "find dist -name '*.js' -type f -exec sh -c 'for f do terser \"$f\" --compress directives=false --mangle --module --comments false --output \"$f\"; done' sh {} +",
26
- "build": "npm run clean && npm run build:ts && npm run minify:js",
27
- "pack:local": "npm pack",
28
- "release": "npm run release:patch",
29
- "release:patch": "npm version patch --no-git-tag-version && npm run release:push",
30
- "release:minor": "npm version minor --no-git-tag-version && npm run release:push",
31
- "release:major": "npm version major --no-git-tag-version && npm run release:push",
32
- "release:push": "npm run build && npm pack --dry-run && VERSION=$(node -p \"require('./package.json').version\") && git add package.json package-lock.json && git commit -m \"chore(sdk): release v$VERSION\" && git tag \"sdk-v$VERSION\" && git push origin HEAD && git push origin \"sdk-v$VERSION\"",
33
- "prepack": "npm run build"
34
- },
35
- "exports": {
36
- ".": {
37
- "types": "./dist/index.d.ts",
38
- "import": "./dist/index.js"
39
- },
40
- "./api": {
41
- "types": "./dist/api/index.d.ts",
42
- "import": "./dist/api/index.js"
43
- },
44
- "./components": {
45
- "types": "./dist/components/index.d.ts",
46
- "import": "./dist/components/index.js"
47
- },
48
- "./mcp": {
49
- "types": "./dist/mcp/index.d.ts",
50
- "import": "./dist/mcp/index.js"
51
- },
52
- "./components/exams": {
53
- "types": "./dist/components/exams/index.d.ts",
54
- "import": "./dist/components/exams/index.js"
55
- },
56
- "./components/exams/take": {
57
- "types": "./dist/components/exams/take/index.d.ts",
58
- "import": "./dist/components/exams/take/index.js"
59
- },
60
- "./components/exams/entry": {
61
- "types": "./dist/components/exams/entry/index.d.ts",
62
- "import": "./dist/components/exams/entry/index.js"
63
- },
64
- "./components/exams/rooms": {
65
- "types": "./dist/components/exams/rooms/index.d.ts",
66
- "import": "./dist/components/exams/rooms/index.js"
67
- },
68
- "./components/questions": {
69
- "types": "./dist/components/questions/index.d.ts",
70
- "import": "./dist/components/questions/index.js"
71
- },
72
- "./components/themes": {
73
- "types": "./dist/components/themes/index.d.ts",
74
- "import": "./dist/components/themes/index.js"
75
- },
76
- "./components/results": {
77
- "types": "./dist/components/results/index.d.ts",
78
- "import": "./dist/components/results/index.js"
79
- },
80
- "./components/questions/*": {
81
- "types": "./dist/components/questions/*.d.ts",
82
- "import": "./dist/components/questions/*.js"
83
- },
84
- "./shared": {
85
- "types": "./dist/shared/index.d.ts",
86
- "import": "./dist/shared/index.js"
87
- },
88
- "./*": {
89
- "types": "./dist/components/questions/*.d.ts",
90
- "import": "./dist/components/questions/*.js"
91
- },
92
- "./package.json": "./package.json"
93
- },
94
- "publishConfig": {
95
- "access": "public"
96
- },
97
- "peerDependencies": {
98
- "next": ">=15",
99
- "react": ">=19",
100
- "react-dom": ">=19"
101
- },
102
- "devDependencies": {
103
- "@types/node": "^25.9.3",
104
- "terser": "^5.48.0",
105
- "tsc-alias": "^1.8.17",
106
- "typescript": "~5.9.2"
107
- },
108
- "dependencies": {
109
- "@dnd-kit/core": "^6.3.1",
110
- "@dnd-kit/sortable": "^10.0.0",
111
- "@dnd-kit/utilities": "^3.2.2",
112
- "@fortawesome/free-solid-svg-icons": "^7.3.1",
113
- "@fortawesome/react-fontawesome": "^3.3.1",
114
- "@hookform/resolvers": "^5.2.2",
115
- "@modelcontextprotocol/sdk": "^1.29.0",
116
- "@radix-ui/react-alert-dialog": "^1.1.15",
117
- "@radix-ui/react-avatar": "^1.1.10",
118
- "@radix-ui/react-checkbox": "^1.3.3",
119
- "@radix-ui/react-collapsible": "^1.1.12",
120
- "@radix-ui/react-dialog": "^1.1.15",
121
- "@radix-ui/react-dropdown-menu": "^2.1.16",
122
- "@radix-ui/react-hover-card": "^1.1.15",
123
- "@radix-ui/react-label": "^2.1.7",
124
- "@radix-ui/react-popover": "^1.1.15",
125
- "@radix-ui/react-progress": "^1.1.7",
126
- "@radix-ui/react-radio-group": "^1.3.8",
127
- "@radix-ui/react-scroll-area": "^1.2.10",
128
- "@radix-ui/react-select": "^2.2.6",
129
- "@radix-ui/react-separator": "^1.1.7",
130
- "@radix-ui/react-slot": "^1.2.3",
131
- "@radix-ui/react-switch": "^1.2.6",
132
- "@radix-ui/react-tabs": "^1.1.13",
133
- "@radix-ui/react-tooltip": "^1.2.8",
134
- "@tanstack/react-query": "^5.90.2",
135
- "@tiptap/extension-table": "^3.22.3",
136
- "@tiptap/extension-table-cell": "^3.22.3",
137
- "@tiptap/extension-table-header": "^3.22.3",
138
- "@tiptap/extension-table-row": "^3.22.3",
139
- "@tiptap/extension-text-align": "^3.22.3",
140
- "@tiptap/pm": "^3.22.3",
141
- "@tiptap/react": "^3.22.3",
142
- "@tiptap/starter-kit": "^3.22.3",
143
- "@types/qrcode": "^1.5.6",
144
- "@xyflow/react": "^12.8.6",
145
- "axios": "^1.12.2",
146
- "class-variance-authority": "^0.7.1",
147
- "clsx": "^2.1.1",
148
- "cmdk": "^1.1.1",
149
- "date-fns": "^4.1.0",
150
- "embla-carousel-react": "^8.6.0",
151
- "howler": "^2.2.4",
152
- "lucide-react": "^0.544.0",
153
- "qrcode": "^1.5.4",
154
- "radix-ui": "^1.4.3",
155
- "react-day-picker": "^9.14.0",
156
- "react-hook-form": "^7.65.0",
157
- "sonner": "^2.0.7",
158
- "tailwind-merge": "^3.3.1",
159
- "vaul": "^1.1.2",
160
- "zod": "^4.1.11",
161
- "zustand": "^5.0.8"
162
- }
163
- }
1
+ {
2
+ "name": "@tinyweb_dev/oe-exam-sdk",
3
+ "version": "0.1.24",
4
+ "description": "Reusable OceanEdu question components.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "bin": {
10
+ "oe-exam-mcp": "./dist/mcp/index.js"
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "README.md"
15
+ ],
16
+ "sideEffects": [
17
+ "**/*.css"
18
+ ],
19
+ "scripts": {
20
+ "clean": "rm -rf dist",
21
+ "build:compile": "tsc -p tsconfig.build.json",
22
+ "build:alias": "tsc-alias -p tsconfig.build.json",
23
+ "build:ts": "npm run build:compile && npm run build:assets && npm run build:alias",
24
+ "build:assets": "for d in cambridge-yle english-certification summer-sky; do [ -d src/components/themes/$d/assets ] && cp -R src/components/themes/$d/assets dist/components/themes/$d/ || true; done; [ -d src/components/shared/audio/assets ] && cp -R src/components/shared/audio/assets dist/components/shared/audio/ || true; [ -d src/components/results/report/assets ] && mkdir -p dist/components/results/report && cp -R src/components/results/report/assets dist/components/results/report/ || true; (cd src && find . -name '*.module.css' -exec cp --parents {} ../dist/ \\;); cp src/global.d.ts dist/global.d.ts",
25
+ "minify:js": "find dist -name '*.js' -type f -exec sh -c 'for f do terser \"$f\" --compress directives=false --mangle --module --comments false --output \"$f\"; done' sh {} +",
26
+ "build": "npm run clean && npm run build:ts && npm run minify:js",
27
+ "pack:local": "npm pack",
28
+ "release": "npm run release:patch",
29
+ "release:patch": "npm version patch --no-git-tag-version && npm run release:push",
30
+ "release:minor": "npm version minor --no-git-tag-version && npm run release:push",
31
+ "release:major": "npm version major --no-git-tag-version && npm run release:push",
32
+ "release:push": "npm run build && npm pack --dry-run && VERSION=$(node -p \"require('./package.json').version\") && git add package.json package-lock.json && git commit -m \"chore(sdk): release v$VERSION\" && git tag \"sdk-v$VERSION\" && git push origin HEAD && git push origin \"sdk-v$VERSION\"",
33
+ "prepack": "npm run build"
34
+ },
35
+ "exports": {
36
+ ".": {
37
+ "types": "./dist/index.d.ts",
38
+ "import": "./dist/index.js"
39
+ },
40
+ "./api": {
41
+ "types": "./dist/api/index.d.ts",
42
+ "import": "./dist/api/index.js"
43
+ },
44
+ "./components": {
45
+ "types": "./dist/components/index.d.ts",
46
+ "import": "./dist/components/index.js"
47
+ },
48
+ "./mcp": {
49
+ "types": "./dist/mcp/index.d.ts",
50
+ "import": "./dist/mcp/index.js"
51
+ },
52
+ "./components/exams": {
53
+ "types": "./dist/components/exams/index.d.ts",
54
+ "import": "./dist/components/exams/index.js"
55
+ },
56
+ "./components/exams/take": {
57
+ "types": "./dist/components/exams/take/index.d.ts",
58
+ "import": "./dist/components/exams/take/index.js"
59
+ },
60
+ "./components/exams/entry": {
61
+ "types": "./dist/components/exams/entry/index.d.ts",
62
+ "import": "./dist/components/exams/entry/index.js"
63
+ },
64
+ "./components/exams/rooms": {
65
+ "types": "./dist/components/exams/rooms/index.d.ts",
66
+ "import": "./dist/components/exams/rooms/index.js"
67
+ },
68
+ "./components/questions": {
69
+ "types": "./dist/components/questions/index.d.ts",
70
+ "import": "./dist/components/questions/index.js"
71
+ },
72
+ "./components/themes": {
73
+ "types": "./dist/components/themes/index.d.ts",
74
+ "import": "./dist/components/themes/index.js"
75
+ },
76
+ "./components/results": {
77
+ "types": "./dist/components/results/index.d.ts",
78
+ "import": "./dist/components/results/index.js"
79
+ },
80
+ "./components/questions/*": {
81
+ "types": "./dist/components/questions/*.d.ts",
82
+ "import": "./dist/components/questions/*.js"
83
+ },
84
+ "./shared": {
85
+ "types": "./dist/shared/index.d.ts",
86
+ "import": "./dist/shared/index.js"
87
+ },
88
+ "./*": {
89
+ "types": "./dist/components/questions/*.d.ts",
90
+ "import": "./dist/components/questions/*.js"
91
+ },
92
+ "./package.json": "./package.json"
93
+ },
94
+ "publishConfig": {
95
+ "access": "public"
96
+ },
97
+ "peerDependencies": {
98
+ "next": ">=15",
99
+ "react": ">=19",
100
+ "react-dom": ">=19"
101
+ },
102
+ "devDependencies": {
103
+ "@types/node": "^25.9.3",
104
+ "terser": "^5.48.0",
105
+ "tsc-alias": "^1.8.17",
106
+ "typescript": "~5.9.2"
107
+ },
108
+ "dependencies": {
109
+ "@dnd-kit/core": "^6.3.1",
110
+ "@dnd-kit/sortable": "^10.0.0",
111
+ "@dnd-kit/utilities": "^3.2.2",
112
+ "@fortawesome/free-solid-svg-icons": "^7.3.1",
113
+ "@fortawesome/react-fontawesome": "^3.3.1",
114
+ "@hookform/resolvers": "^5.2.2",
115
+ "@modelcontextprotocol/sdk": "^1.29.0",
116
+ "@radix-ui/react-alert-dialog": "^1.1.15",
117
+ "@radix-ui/react-avatar": "^1.1.10",
118
+ "@radix-ui/react-checkbox": "^1.3.3",
119
+ "@radix-ui/react-collapsible": "^1.1.12",
120
+ "@radix-ui/react-dialog": "^1.1.15",
121
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
122
+ "@radix-ui/react-hover-card": "^1.1.15",
123
+ "@radix-ui/react-label": "^2.1.7",
124
+ "@radix-ui/react-popover": "^1.1.15",
125
+ "@radix-ui/react-progress": "^1.1.7",
126
+ "@radix-ui/react-radio-group": "^1.3.8",
127
+ "@radix-ui/react-scroll-area": "^1.2.10",
128
+ "@radix-ui/react-select": "^2.2.6",
129
+ "@radix-ui/react-separator": "^1.1.7",
130
+ "@radix-ui/react-slot": "^1.2.3",
131
+ "@radix-ui/react-switch": "^1.2.6",
132
+ "@radix-ui/react-tabs": "^1.1.13",
133
+ "@radix-ui/react-tooltip": "^1.2.8",
134
+ "@tanstack/react-query": "^5.90.2",
135
+ "@tiptap/extension-table": "^3.22.3",
136
+ "@tiptap/extension-table-cell": "^3.22.3",
137
+ "@tiptap/extension-table-header": "^3.22.3",
138
+ "@tiptap/extension-table-row": "^3.22.3",
139
+ "@tiptap/extension-text-align": "^3.22.3",
140
+ "@tiptap/pm": "^3.22.3",
141
+ "@tiptap/react": "^3.22.3",
142
+ "@tiptap/starter-kit": "^3.22.3",
143
+ "@types/qrcode": "^1.5.6",
144
+ "@xyflow/react": "^12.8.6",
145
+ "axios": "^1.12.2",
146
+ "class-variance-authority": "^0.7.1",
147
+ "clsx": "^2.1.1",
148
+ "cmdk": "^1.1.1",
149
+ "date-fns": "^4.1.0",
150
+ "embla-carousel-react": "^8.6.0",
151
+ "howler": "^2.2.4",
152
+ "lucide-react": "^0.544.0",
153
+ "qrcode": "^1.5.4",
154
+ "radix-ui": "^1.4.3",
155
+ "react-day-picker": "^9.14.0",
156
+ "react-hook-form": "^7.65.0",
157
+ "sonner": "^2.0.7",
158
+ "tailwind-merge": "^3.3.1",
159
+ "vaul": "^1.1.2",
160
+ "zod": "^4.1.11",
161
+ "zustand": "^5.0.8"
162
+ }
163
+ }
@@ -1,12 +0,0 @@
1
- import { z } from 'zod';
2
- declare const schema: z.ZodObject<{
3
- OE_API_URL: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
4
- OE_API_TOKEN: z.ZodString;
5
- OE_HTTP_TIMEOUT_MS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
6
- }, z.core.$strip>;
7
- export type McpConfig = z.infer<typeof schema> & {
8
- packageName: string;
9
- packageVersion: string;
10
- };
11
- export declare function loadMcpConfig(): McpConfig;
12
- export {};
@@ -1 +0,0 @@
1
- import{readFileSync as e}from"node:fs";import{dirname as r,join as o}from"node:path";import{fileURLToPath as n}from"node:url";import{z as t}from"zod";const s=function(){try{const t=r(n(import.meta.url));return JSON.parse(e(o(t,"..","..","package.json"),"utf8"))}catch{return{name:"@tinyweb_dev/oe-exam-sdk",version:"0.0.0"}}}(),a=t.object({OE_API_URL:t.string().url().transform(e=>e.replace(/\/+$/,"")),OE_API_TOKEN:t.string().min(1),OE_HTTP_TIMEOUT_MS:t.coerce.number().int().positive().default(15e3)});let i=null;export function loadMcpConfig(){if(i)return i;const e=a.safeParse(process.env);if(!e.success){const r=e.error.issues.map(e=>` • ${e.path.join(".")||"<root>"}: ${e.message}`).join("\n");console.error(`\n[oe-exam-mcp] Configuration error:\n${r}\n\nRequired: OE_API_URL and OE_API_TOKEN.\n`),process.exit(1)}return i={...e.data,packageName:s.name,packageVersion:s.version},i}
@@ -1,6 +0,0 @@
1
- export interface McpRequestOptions<TBody = unknown> {
2
- method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
3
- url: string;
4
- body?: TBody;
5
- }
6
- export declare function mcpRequest<T>(opts: McpRequestOptions): Promise<T>;
@@ -1 +0,0 @@
1
- import{ErrorCode as t,McpError as o}from"@modelcontextprotocol/sdk/types.js";import{loadMcpConfig as r}from"./config.js";export async function mcpRequest(n){const s=r(),a=new AbortController,i=setTimeout(()=>a.abort(),s.OE_HTTP_TIMEOUT_MS);try{const r=await fetch(`${s.OE_API_URL}${n.url}`,{method:n.method,signal:a.signal,headers:{Accept:"application/json","Content-Type":"application/json",...e(s.OE_API_TOKEN)},body:void 0===n.body?void 0:JSON.stringify(n.body)}),i=await r.text(),c=i?JSON.parse(i):void 0;if(!r.ok){const e=function(t){if(!t||"object"!=typeof t)return;const o=t.message;return Array.isArray(o)?o.join("; "):"string"==typeof o?o:void 0}(c)??r.statusText,s=400===r.status||404===r.status||422===r.status?t.InvalidParams:t.InternalError;throw new o(s,`[${n.method} ${n.url}] ${r.status}: ${e}`)}return c}catch(r){if(r instanceof o)throw r;throw new o(t.InternalError,r instanceof Error?r.message:"Unknown HTTP error")}finally{clearTimeout(i)}}function e(t){return t?t.startsWith("oeexam_")?{"X-API-Key":t}:{Authorization:`Bearer ${t}`}:{}}
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env node
2
- export declare function startOeExamMcpServer(): Promise<void>;
3
- export { loadMcpConfig } from './config.js';
4
- export { registerAllMcpTools } from './tools/index.js';
5
- export { registerExamTools } from './tools/exams.js';
6
- export { registerResultTools } from './tools/results.js';
package/dist/mcp/index.js DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- import{McpServer as e}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as o}from"@modelcontextprotocol/sdk/server/stdio.js";import{loadMcpConfig as r}from"./config.js";import{registerAllMcpTools as s}from"./tools/index.js";export async function startOeExamMcpServer(){const t=r(),a=new e({name:t.packageName,version:t.packageVersion},{capabilities:{tools:{},logging:{}},instructions:"OE Exam MCP server. Use exam_json_template_generate then exam_create_from_json to create an exam, and exam_result_get to view a result review."});s(a),await a.connect(new o);const c=async()=>{await a.close().catch(e=>console.error("[oe-exam-mcp] server.close failed",e)),process.exit(0)};process.on("SIGINT",()=>{c()}),process.on("SIGTERM",()=>{c()})}import.meta.url===`file://${process.argv[1]}`&&startOeExamMcpServer().catch(e=>{console.error("[oe-exam-mcp] Fatal startup error",e),process.exit(1)});export{loadMcpConfig}from"./config.js";export{registerAllMcpTools}from"./tools/index.js";export{registerExamTools}from"./tools/exams.js";export{registerResultTools}from"./tools/results.js";
@@ -1,4 +0,0 @@
1
- import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
- export declare function buildToolResult<T>(value: T): CallToolResult;
3
- export declare function wrapHandler<TInput extends Record<string, unknown>>(toolName: string, fn: (input: TInput) => Promise<CallToolResult>): (input: TInput) => Promise<CallToolResult>;
4
- export declare function unwrapData<T>(value: unknown): T;
@@ -1 +0,0 @@
1
- import{ErrorCode as t,McpError as r}from"@modelcontextprotocol/sdk/types.js";export function buildToolResult(t){return{content:[{type:"text",text:"string"==typeof t?t:JSON.stringify(t,null,2)}],...t&&"object"==typeof t?{structuredContent:t}:{}}}export function wrapHandler(e,o){return async n=>{try{return await o(n)}catch(o){if(o instanceof r)throw o;throw console.error(`[oe-exam-mcp] ${e} failed`,o),new r(t.InternalError,o instanceof Error?o.message:"Unknown error")}}}export function unwrapData(t){return t&&"object"==typeof t&&"data"in t?t.data:t}
@@ -1,2 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- export declare function registerExamTools(server: McpServer): void;
@@ -1 +0,0 @@
1
- import{mkdir as e,writeFile as t}from"node:fs/promises";import{dirname as n,resolve as i}from"node:path";import{z as a}from"zod";import{mcpRequest as o}from"../http-client.js";import{buildToolResult as r,unwrapData as s,wrapHandler as l}from"./_helpers.js";const p=a.record(a.string(),a.unknown()).describe("Full API question payload or short form: { question, options, answer, points?, partId?, partNo? }."),u=(a.record(a.string(),a.unknown()).describe("Part payload: { name?, partId?, questions: [...] }."),a.record(a.string(),a.unknown()).describe("Template payload accepted by PATCH /manage/exams/:id/template.")),m=a.enum(["PRE_A1","A1","A2","B1","B2","C1","C2"]);export function registerExamTools(g){g.registerTool("exam_templates_search",{title:"Search exam templates",description:"Search available OE Exam templates. Use this before exam_create_with_questions to choose examTemplateId.",inputSchema:{search:a.string().optional().describe("Template name keyword."),page:a.number().int().positive().default(1).describe("1-based page number."),limit:a.number().int().positive().max(50).default(9).describe("Page size sent as API size.")}},l("exam_templates_search",async e=>{const t=new URLSearchParams({page:String(e.page??1),size:String(e.limit??9)});e.search&&t.set("filter",`name:like:${e.search}`);const n=await o({method:"GET",url:`/api/v1/exam-templates?${t}`});return r({templates:k(n)})})),g.registerTool("exam_template_get",{title:"Get exam template",description:"Get one OE Exam template by id. The returned templatePayload can be passed as template to exam_create_with_questions.",inputSchema:{id:a.string().min(1).describe("Template id from exam_templates_search.")}},l("exam_template_get",async e=>{const t=s(await o({method:"GET",url:`/api/v1/exam-templates/${encodeURIComponent(String(e.id))}`}));return r({template:t,templatePayload:P(t)})})),g.registerTool("exam_question_template_get",{title:"Get question payload template",description:"Return easy-to-fill question JSON templates for exam_create_with_questions. Use this before creating questions if you need the expected shape.",inputSchema:{templateId:a.string().min(1).optional().describe("Template id. If provided, generate question payloads from template.parts."),type:a.string().default("CHOOSE_THE_CORRECT_ANSWER").describe("Fallback question type when templateId is omitted."),count:a.number().int().positive().max(100).default(1).describe("Fallback count when templateId is omitted."),partId:a.union([a.string(),a.number()]).default(1),partNo:a.number().int().positive().default(1),skill:a.string().default("READING")}},l("exam_question_template_get",async e=>{const t="string"==typeof e.templateId?s(await o({method:"GET",url:`/api/v1/exam-templates/${encodeURIComponent(e.templateId)}`})):void 0;return r({usage:"Pass questions directly into exam_create_with_questions.questions. Fill content/correctAnswer for each generated API-format payload.",templateId:t?.id,questions:t?b(t):S(e)})})),g.registerTool("exam_json_template_generate",{title:"Get exam JSON template",description:"Generate a user-friendly examJson skeleton from templateTitle or templateId. Edit this JSON, then pass it to exam_create_from_json.",inputSchema:{templateTitle:a.string().optional().describe("Template title to search, e.g. Achievers 2026 Round 1."),templateId:a.string().optional().describe("Exact template id. Preferred when known."),name:a.string().optional().describe("Exam name to put in the JSON."),examLevel:m.optional().describe("Optional exam level for the generated JSON."),difficulty:a.enum(["EASY","MEDIUM","HARD"]).optional().describe("Optional difficulty for the generated JSON."),durationMinutes:a.number().int().positive().optional().describe("Optional duration for the generated JSON.")}},l("exam_json_template_generate",async a=>{const o=await f({templateId:a.templateId,templateTitle:a.templateTitle}),s=function(e,t){const n=Array.isArray(e.parts)?e.parts.filter(c):[];return{name:v(t.name,`${e.title??e.name??"OE Exam"} Draft`),templateId:e.id,templateTitle:e.title??e.name,totalScore:O(e.totalScore??e.total_questions,y(n)),examLevel:_(t.examLevel)??_(e.examLevel??e.exam_level??e.level)??"A1",difficulty:E(t.difficulty)??"MEDIUM",durationMinutes:O(t.durationMinutes,45),isComplete:!1,isActive:!1,parts:n.map((e,t)=>function(e,t){const n=O(e.startIndex,0),i=O(e.questionCount,O(e.endIndex,n)-n+1);return{id:e.id??t+1,name:e.name??`Part ${t+1}`,questionType:v(e.questionType,"CHOOSE_THE_CORRECT_ANSWER"),skill:v(e.skill,"READING"),questions:Array.from({length:Math.max(i,0)},(t,i)=>function(e,t){const n=v(e.questionType,"CHOOSE_THE_CORRECT_ANSWER"),i=(Array.isArray(e.defaultValue)&&e.defaultValue.length>0?e.defaultValue:Array.from({length:O(e.optionCount,4)},(e,t)=>String.fromCharCode(65+t))).map(String);return n.includes("FILL")||n.includes("WRITE")?{question:`Question ${t}`,answer:""}:n.includes("SPEAK")?{prompt:`Question ${t}`,expectedAnswer:""}:{question:`Question ${t}`,options:i,answer:i[0]??"A"}}(e,n+i+1))}}(e,t))}}(o,a),l=JSON.stringify(s,null,2),p=i(`${u=String(a.name??o.title??o.name??"exam"),u.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"").slice(0,80)||"exam"}.json`);var u;return await e(n(p),{recursive:!0}),await t(p,`${l}\n`,"utf8"),r({usage:"Edit filePath, then call exam_create_from_json with the edited JSON string.",templateId:o.id,templateTitle:o.title??o.name,filePath:p,examJson:s,examJsonString:l})})),g.registerTool("exam_create_from_json",{title:"Create exam from JSON",description:"Create an exam from a user-friendly JSON payload grouped by parts. Searches template by title, patches template, maps questions to template.parts, then creates questions.",inputSchema:{examJson:a.string().optional().describe("JSON string with { name, templateTitle/templateId, parts: [{ questions: [...] }] }. Use this for copy/paste."),exam:a.record(a.string(),a.unknown()).optional().describe("Same payload as object if the client supports object arguments.")}},l("exam_create_from_json",async e=>{const t=function(e,t){if(c(t))return t;if("string"!=typeof e||!e.trim())throw new Error("examJson or exam is required");const n=JSON.parse(e);if(!c(n))throw new Error("examJson must be a JSON object");return n}(e.examJson,e.exam),n=await f(t),i=function(e,t){const n=Array.isArray(t.parts)?t.parts.filter(c):[],i=Array.isArray(e.parts)?e.parts.filter(c):[{questions:e.questions}];return i.flatMap((e,t)=>{const i=function(e,t,n){const i=A(e.partId??e.id),a=A(e.name);return t.find(e=>String(e.id)===i)??t.find(e=>"string"==typeof e.name&&e.name===a)??t[n]??{id:n+1,questionType:"CHOOSE_THE_CORRECT_ANSWER",skill:"READING",optionCount:4}}(e,n,t);return(Array.isArray(e.questions)?e.questions.filter(c):[]).map((e,n)=>function(e,t,n,i){const a=O(t.startIndex,0),o=w({questionType:v(e.questionType??e.type??t.questionType,"CHOOSE_THE_CORRECT_ANSWER"),questionNumber:O(e.questionNumber,a+i+1),partId:String(e.partId??t.id??n+1),partNo:O(e.partNo,n+1),sectionId:R(e.sectionId??t.sectionId),skill:v(e.questionSkillType??e.skill??t.skill,"READING"),optionCount:O(t.optionCount,4),defaultValue:Array.isArray(t.defaultValue)?t.defaultValue:void 0});return q(function(e){const t=c(e.content)?{...e.content}:{};"string"==typeof e.question&&(t.question=e.question);"string"==typeof e.text&&(t.question=e.text);Array.isArray(e.options)&&(t.optionType=t.optionType??"text",t.options=e.options);return d({...e,content:c(t)?t:e.content,correctAnswer:void 0===e.answer&&void 0===e.expectedAnswer?e.correctAnswer:x(e.questionType,e.answer??e.expectedAnswer,t.options)})}({...o,...e}),i)}(e,i,t,n))})}(t,n),a=function(e,t,n){return{exam:d({name:v(e.name,"New OE Exam"),code:A(e.code)??h(v(e.name,"New OE Exam")),totalScore:O(e.totalScore,n.length||100),description:A(e.description)??A(e.templateTitle)??"",isComplete:T(e.isComplete,!1),isActive:T(e.isActive,!1),examTemplateId:t,examLevel:_(e.examLevel),difficulty:E(e.difficulty),durationMinutes:R(e.durationMinutes)})}}(t,n.id,i),l=s(await o({method:"POST",url:"/api/v1/manage/exams",body:a.exam}));await o({method:"PATCH",url:`/api/v1/manage/exams/${encodeURIComponent(l.id)}/template`,body:{template:P(n)}});const p=0===i.length?null:await o({method:"POST",url:"/api/v1/manage/questions/batch",body:{examId:l.id,questions:i}});return r({exam:l,templateId:n.id,questions:p,generatedQuestionCount:i.length})})),g.registerTool("exam_create_with_questions",{title:"Create exam with questions",description:"Create an exam, optionally patch template, then bulk create questions via /api/v1/manage/questions/batch. If you do not know examTemplateId/template, call exam_templates_search then exam_template_get first.",inputSchema:{name:a.string().min(1),code:a.string().min(1).optional(),examLevel:m.optional().describe("Valid exam level only: PRE_A1, A1, A2, B1, B2, C1, C2. Do not use template title/id here."),difficulty:a.enum(["EASY","MEDIUM","HARD"]).optional(),totalScore:a.number().nonnegative().optional(),durationMinutes:a.number().int().positive().optional(),description:a.string().optional(),isComplete:a.boolean().optional(),isActive:a.boolean().optional(),examTemplateId:a.string().min(1).optional(),template:u.optional(),questions:a.array(p).default([])}},l("exam_create_with_questions",async e=>{const{template:t,questions:n,...i}=e,a=s(await o({method:"POST",url:"/api/v1/manage/exams",body:d(i)})),l=c(t)?t:await async function(e){if("string"!=typeof e||!e)return;const t=s(await o({method:"GET",url:`/api/v1/exam-templates/${encodeURIComponent(e)}`}));return P(t)}(i.examTemplateId);c(l)&&await o({method:"PATCH",url:`/api/v1/manage/exams/${encodeURIComponent(a.id)}/template`,body:{template:l}});const p=n.map(q),u=0===p.length?null:await o({method:"POST",url:"/api/v1/manage/questions/batch",body:{examId:a.id,questions:p}});return r({exam:a,questions:u})}))}function d(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e))}function c(e){return Boolean(e&&"object"==typeof e&&!Array.isArray(e)&&Object.keys(e).length>0)}async function f(e){const t=A(e.templateId);if(t)return s(await o({method:"GET",url:`/api/v1/exam-templates/${encodeURIComponent(t)}`}));const n=A(e.templateTitle);if(!n)throw new Error("templateTitle or templateId is required");const i=new URLSearchParams({page:"1",size:"10",filter:`name:like:${n}`}),a=k(await o({method:"GET",url:`/api/v1/exam-templates?${i}`})),r=a.find(e=>(e.title??e.name)===n),l=r??a[0];if(!l?.id)throw new Error(`No exam template found for title: ${n}`);return s(await o({method:"GET",url:`/api/v1/exam-templates/${encodeURIComponent(l.id)}`}))}function y(e){return e.reduce((e,t)=>e+O(t.questionCount,0),0)}function x(e,t,n){if(void 0===t)return;const i=String(e??"");return i.includes("FILL")||i.includes("WRITE")?{answers:[String(t)]}:i.includes("SPEAK")?{expectedAnswer:String(t)}:{answer:g(t,n)}}function g(e,t){if("string"==typeof e&&Array.isArray(t)){const n=t.map(String).findIndex(t=>t===e);if(n>=0)return n}return I(e)}function h(e){return e.toUpperCase().replace(/[^A-Z0-9]+/g,"_").replace(/^_|_$/g,"").slice(0,40)||"OE_EXAM"}function A(e){return"string"==typeof e&&e?e:void 0}function T(e,t){return"boolean"==typeof e?e:t}function _(e){return"string"==typeof e&&m.safeParse(e).success?e:void 0}function E(e){return"string"==typeof e&&["EASY","MEDIUM","HARD"].includes(e)?e:void 0}function q(e,t){if(e.questionType)return d({...e,partId:void 0===e.partId?void 0:String(e.partId),partNo:e.partNo??1,questionNumber:e.questionNumber??t+1,points:e.points??1,addToQuestionBank:e.addToQuestionBank??!1,isExample:e.isExample??!1});const n=Array.isArray(e.options)?e.options:["A","B","C","D"];return d({questionType:e.type??"CHOOSE_THE_CORRECT_ANSWER",content:e.content??{question:String(e.question??e.text??`Question ${t+1}`),optionType:"text",options:n},correctAnswer:e.correctAnswer??{answer:I(e.answer??e.correctAnswerIndex??0)},points:e.points??1,explanation:e.explanation??"",questionNumber:e.questionNumber??t+1,partId:String(e.partId??1),partNo:e.partNo??1,sectionId:e.sectionId,questionSkillType:e.questionSkillType??e.skill??"READING",addToQuestionBank:e.addToQuestionBank??!1,isExample:e.isExample??!1})}function I(e){if("number"==typeof e)return e;if("string"==typeof e&&/^[A-E]$/i.test(e))return e.toUpperCase().charCodeAt(0)-65;const t=Number(e);return Number.isFinite(t)?t:0}function S(e){return Array.from({length:Number(e.count??1)},(t,n)=>w({questionType:String(e.type??"CHOOSE_THE_CORRECT_ANSWER"),questionNumber:n+1,partId:String(e.partId??1),partNo:Number(e.partNo??1),skill:String(e.skill??"READING")}))}function b(e){return(Array.isArray(e.parts)?e.parts.filter(c):[]).flatMap((e,t)=>{const n=O(e.startIndex,0),i=O(e.questionCount,O(e.endIndex,n)-n+1);return Array.from({length:Math.max(i,0)},(i,a)=>w({questionType:v(e.questionType,"CHOOSE_THE_CORRECT_ANSWER"),questionNumber:n+a+1,partId:String(e.id??t+1),partNo:t+1,sectionId:R(e.sectionId),skill:v(e.skill,"READING"),optionCount:O(e.optionCount,4),defaultValue:Array.isArray(e.defaultValue)?e.defaultValue:void 0}))})}function w(e){return d({questionType:e.questionType,content:C(e.questionType,e.questionNumber,e.optionCount,e.defaultValue),correctAnswer:N(e.questionType),points:1,explanation:"",questionNumber:e.questionNumber,partId:e.partId,partNo:e.partNo,sectionId:e.sectionId,questionSkillType:e.skill,addToQuestionBank:!1,isExample:!1})}function C(e,t,n=4,i){const a=(i?.length?i:Array.from({length:n},(e,t)=>String.fromCharCode(65+t))).map(String);return e.includes("FILL")||e.includes("WRITE")?{question:`Question ${t}`,text:""}:e.includes("SPEAK")?{prompt:`Question ${t}`}:e.includes("LISTEN")?{question:`Question ${t}`,audioUrl:"",optionType:"text",options:a}:{question:`Question ${t}`,optionType:"text",options:a}}function N(e){return e.includes("FILL")||e.includes("WRITE")?{answers:[""]}:e.includes("SPEAK")?{expectedAnswer:""}:{answer:0}}function v(e,t){return"string"==typeof e&&e?e:t}function O(e,t){const n=Number(e);return Number.isFinite(n)?n:t}function R(e){const t=Number(e);return Number.isFinite(t)?t:void 0}function k(e){const t=s(e);return Array.isArray(t)?t:t&&"object"==typeof t&&"data"in t&&Array.isArray(t.data)?t.data:[]}function P(e){const t=Array.isArray(e.parts)?e.parts:[];return d({id:e.id,title:e.title??e.name,subTitle:e.subTitle??e.sub_title??"",theme:e.theme,totalQuestions:e.totalQuestions??e.total_questions,totalParts:e.totalParts??e.total_parts??t.length,description:e.description??"",examLevel:e.examLevel??e.exam_level??e.level??"",type:e.type??null,parts:t})}
@@ -1,2 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- export declare function registerAllMcpTools(server: McpServer): void;
@@ -1 +0,0 @@
1
- import{registerExamTools as o}from"./exams.js";import{registerResultTools as r}from"./results.js";export function registerAllMcpTools(s){o(s),r(s)}
@@ -1,2 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- export declare function registerResultTools(server: McpServer): void;
@@ -1 +0,0 @@
1
- import{z as t}from"zod";import{mcpRequest as e}from"../http-client.js";import{buildToolResult as n,wrapHandler as o}from"./_helpers.js";export function registerResultTools(r){r.registerTool("exam_result_get",{title:"Get exam result review",description:"Fetch student exam result review by attemptId. Optional token supports public result review links.",inputSchema:{attemptId:t.string().min(1),token:t.string().min(1).optional(),includeAttempt:t.boolean().default(!0)}},o("exam_result_get",async({attemptId:t,token:o,includeAttempt:r})=>{const i=o?`?token=${encodeURIComponent(String(o))}`:"",s=await e({method:"GET",url:`/api/v1/student/results/${encodeURIComponent(String(t))}${i}`}),l=!1===r?null:await e({method:"GET",url:`/api/v1/student/attempts/${encodeURIComponent(String(t))}${i}`});return n({result:s,attempt:l})}))}