ronin-skillforce 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/LICENSE +21 -0
- package/README.md +225 -2
- package/bin/cli.js +811 -0
- package/bin/commands/check.js +298 -0
- package/bin/commands/create.js +169 -0
- package/bin/commands/doctor.js +392 -0
- package/bin/commands/import.js +557 -0
- package/bin/commands/next.js +125 -0
- package/bin/commands/review.js +207 -0
- package/bin/commands/status.js +114 -0
- package/bin/commands/studio.js +497 -0
- package/bin/commands/trail.js +211 -0
- package/bin/commands/web.js +350 -0
- package/bin/course-reader.js +611 -0
- package/bin/node-version.js +60 -0
- package/bin/platforms/_shared.js +211 -0
- package/bin/platforms/claude.js +104 -0
- package/bin/platforms/cursor.js +49 -0
- package/bin/platforms/gemini.js +65 -0
- package/bin/platforms/opencode.js +95 -0
- package/bin/registry-helpers.js +504 -0
- package/bin/static-server.js +153 -0
- package/bin/studio-store.js +94 -0
- package/bin/sync-shared.sh +5 -0
- package/package.json +91 -4
- package/plugin.json +139 -0
- package/shared/SKILL.md +568 -0
- package/shared/agents/coach.md +225 -0
- package/shared/agents/navigator.md +215 -0
- package/shared/agents/professor.md +372 -0
- package/shared/agents/researcher.md +181 -0
- package/shared/agents/spotter.md +129 -0
- package/shared/commands/professor/archive.md +103 -0
- package/shared/commands/professor/capstone-review.md +79 -0
- package/shared/commands/professor/capstone.md +34 -0
- package/shared/commands/professor/discuss.md +18 -0
- package/shared/commands/professor/done.md +44 -0
- package/shared/commands/professor/export.md +69 -0
- package/shared/commands/professor/hint.md +108 -0
- package/shared/commands/professor/navigator.md +67 -0
- package/shared/commands/professor/new-topic.md +73 -0
- package/shared/commands/professor/next.md +174 -0
- package/shared/commands/professor/note.md +63 -0
- package/shared/commands/professor/onboard.md +95 -0
- package/shared/commands/professor/progress.md +46 -0
- package/shared/commands/professor/publish.md +93 -0
- package/shared/commands/professor/quiz.md +27 -0
- package/shared/commands/professor/recall.md +74 -0
- package/shared/commands/professor/review.md +43 -0
- package/shared/commands/professor/schedule.md +75 -0
- package/shared/commands/professor/skill-export.md +87 -0
- package/shared/commands/professor/spotter.md +76 -0
- package/shared/commands/professor/stuck.md +27 -0
- package/shared/commands/professor/switch.md +50 -0
- package/shared/commands/professor/syllabus.md +9 -0
- package/shared/commands/professor/template-export.md +91 -0
- package/shared/commands/professor/template-import.md +111 -0
- package/shared/commands/professor/worktrees.md +58 -0
- package/shared/hooks/pre-compact.js +232 -0
- package/studio/README.md +313 -0
- package/studio/dist/assets/BetaReportPanel-8NrDLiPm.js +1 -0
- package/studio/dist/assets/Capstone-Cv9kH6LY.js +6 -0
- package/studio/dist/assets/CodeWorkspace-BwCEINWo.js +2 -0
- package/studio/dist/assets/Course-BJ_hafUP.js +1 -0
- package/studio/dist/assets/Editor-CV0mNhlz.js +53 -0
- package/studio/dist/assets/Home-C1ry89ak.js +1 -0
- package/studio/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/studio/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/studio/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/studio/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/studio/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/studio/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/studio/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/studio/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/studio/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/studio/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/studio/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/studio/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/studio/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/studio/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/studio/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/studio/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/studio/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/studio/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/studio/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/studio/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/studio/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/studio/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/studio/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/studio/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/studio/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/studio/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/studio/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/studio/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/studio/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/studio/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/studio/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/studio/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/studio/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/studio/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/studio/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/studio/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/studio/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/studio/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/studio/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/studio/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/studio/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/studio/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/studio/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/studio/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/studio/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/studio/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/studio/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/studio/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/studio/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/studio/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/studio/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/studio/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/studio/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/studio/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/studio/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/studio/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/studio/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/studio/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/studio/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/studio/dist/assets/Lesson-2e9TD9yL.js +2 -0
- package/studio/dist/assets/NewCourse-BZeOg2xn.js +952 -0
- package/studio/dist/assets/Progress-CRc_xEWC.js +1 -0
- package/studio/dist/assets/Review-6QEbHEfk.js +1 -0
- package/studio/dist/assets/Settings-NFnqVQui.js +1 -0
- package/studio/dist/assets/TutorPanel-DXQ6Z3BY.js +123 -0
- package/studio/dist/assets/abnfDiagram-WEYOIA3C-qeSjKlGM.js +1 -0
- package/studio/dist/assets/anthropic-D7NiQCUH.js +15 -0
- package/studio/dist/assets/arc-Y1ARJVZ4.js +1 -0
- package/studio/dist/assets/architecture-WOLXFQ4H-COG-m35l.js +1 -0
- package/studio/dist/assets/architectureDiagram-ZHVYKT53-VO7uKoeD.js +36 -0
- package/studio/dist/assets/array-BifhSqXX.js +1 -0
- package/studio/dist/assets/blockDiagram-RJAE7QA3-BGJF_qiL.js +140 -0
- package/studio/dist/assets/bridge-BTh0AeNX.js +1 -0
- package/studio/dist/assets/c4Diagram-XDPPPE54-C-S10RgU.js +38 -0
- package/studio/dist/assets/capstone-Amn6Hztq.js +135 -0
- package/studio/dist/assets/card-store-BrbrtX9w.js +1 -0
- package/studio/dist/assets/channel-SJiUQu_N.js +1 -0
- package/studio/dist/assets/chunk-2E7WHVMD-fv-iqTyC.js +88 -0
- package/studio/dist/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- package/studio/dist/assets/chunk-4EMNKR2T-CY9IZrgv.js +1 -0
- package/studio/dist/assets/chunk-55XCU76K-DyTZz7v_.js +1 -0
- package/studio/dist/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- package/studio/dist/assets/chunk-7S6XSTFO-BNMzTLRW.js +2 -0
- package/studio/dist/assets/chunk-AQDTSKZB-CEYANohH.js +72 -0
- package/studio/dist/assets/chunk-DSNLSHJZ-CDk5EXjH.js +129 -0
- package/studio/dist/assets/chunk-DUW6YSOI-Dgx8z5s3.js +1 -0
- package/studio/dist/assets/chunk-HAGFOTED-hNAi1zh_.js +62 -0
- package/studio/dist/assets/chunk-HNE4Q36P-COJnSYoZ.js +1 -0
- package/studio/dist/assets/chunk-J5ZVWO5B-oDCd-gWO.js +1 -0
- package/studio/dist/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
- package/studio/dist/assets/chunk-NGNAAXSQ-C2Wy9Xmz.js +136 -0
- package/studio/dist/assets/chunk-NQOKRVIF-BU6KVxRr.js +213 -0
- package/studio/dist/assets/chunk-UHW7PFBP-DYvxL9JG.js +1 -0
- package/studio/dist/assets/chunk-VQ5W62QI-CuRfk65j.js +1 -0
- package/studio/dist/assets/chunk-WQQL4CDK-KaG7x-Mb.js +10 -0
- package/studio/dist/assets/chunk-XXDRQBXY-CnbRkih_.js +1 -0
- package/studio/dist/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- package/studio/dist/assets/chunk-YZO67HFG-DbuqIZmk.js +1 -0
- package/studio/dist/assets/chunk-ZAUX2LTX-bk4ivUmc.js +1 -0
- package/studio/dist/assets/chunk-ZCHMQSOR-B_LElfwh.js +2 -0
- package/studio/dist/assets/classDiagram-v2-4MYWR3UA-DtCJ6H_X.js +217 -0
- package/studio/dist/assets/cose-bilkent-JH36ORCC-DMBAZFnj.js +1 -0
- package/studio/dist/assets/course-export-DpaOTT3a.js +1 -0
- package/studio/dist/assets/cynefin-EF2NZ3EQ-D9btHJvU.js +1 -0
- package/studio/dist/assets/cynefinDiagram-WF24UDRP-CsFJuv27.js +62 -0
- package/studio/dist/assets/cytoscape.esm-CHBj4VkZ.js +321 -0
- package/studio/dist/assets/dagre-NK4GIZVX-Cg1Duec7.js +4 -0
- package/studio/dist/assets/defaultLocale-BFoDCU3G.js +1 -0
- package/studio/dist/assets/diagram-CBK3MWTC-_KU2m5gd.js +24 -0
- package/studio/dist/assets/diagram-I5XIIV2D-5RaBrwUA.js +24 -0
- package/studio/dist/assets/diagram-ODIL2UCW-C0VKxoD3.js +30 -0
- package/studio/dist/assets/diagram-PFBEJUXE-BaHcZEP2.js +3 -0
- package/studio/dist/assets/diagram-RJA3I4VV-cwSAZ90p.js +41 -0
- package/studio/dist/assets/diagram-TYUNFSNR-BJImxCEN.js +200 -0
- package/studio/dist/assets/dist-CumnGVmJ.js +1 -0
- package/studio/dist/assets/ebnfDiagram-V4P3TTPT-OQQLRXcm.js +1 -0
- package/studio/dist/assets/elk-QONTBCTQ-BJGhQHxQ.js +27 -0
- package/studio/dist/assets/erDiagram-KYQIGQAA-D91EKSep.js +99 -0
- package/studio/dist/assets/error-BTdmRKoB.js +1 -0
- package/studio/dist/assets/errors-BZa_CNHL.js +1 -0
- package/studio/dist/assets/eventmodeling-K75KTNOO-DgdTRgnw.js +1 -0
- package/studio/dist/assets/fetch-local-DG3O3hmQ.js +4 -0
- package/studio/dist/assets/flowDiagram-IUGLLWBU-CVnJYMum.js +1 -0
- package/studio/dist/assets/ganttDiagram-R7GPNTJY-CSYLPWEI.js +292 -0
- package/studio/dist/assets/gitGraph-VSP46ZUC-D6GWaV9y.js +1 -0
- package/studio/dist/assets/gitGraphDiagram-PDYL7XHY-CaV31bvT.js +106 -0
- package/studio/dist/assets/graphlib-DS17s2tU.js +1 -0
- package/studio/dist/assets/index-CVenTTOY.css +2 -0
- package/studio/dist/assets/index-DioXTZVc.js +313 -0
- package/studio/dist/assets/info-OHQRW6UA-BN-x8GoN.js +1 -0
- package/studio/dist/assets/infoDiagram-MVDRKAX5-CIose_cy.js +2 -0
- package/studio/dist/assets/init-C-OQMol4.js +1 -0
- package/studio/dist/assets/ishikawaDiagram-HPEHV2GE-DCihSNBw.js +70 -0
- package/studio/dist/assets/journeyDiagram-BOSHW7O7-Bz3o3RIX.js +139 -0
- package/studio/dist/assets/js.worker-AD7DjksO.js +4 -0
- package/studio/dist/assets/kanban-definition-FNSA4HC5-BUyqvlFE.js +89 -0
- package/studio/dist/assets/katex-B_JcIwqj.js +257 -0
- package/studio/dist/assets/katex-BgsA2lO1.css +1 -0
- package/studio/dist/assets/line-B08T8KlK.js +1 -0
- package/studio/dist/assets/linear-C9iwaMOi.js +1 -0
- package/studio/dist/assets/mermaid-parser.core-BYwreSUy.js +7 -0
- package/studio/dist/assets/mermaid.core-DTO1oDmq.js +44 -0
- package/studio/dist/assets/mindmap-definition-6OY57UG3-BGLwKheY.js +98 -0
- package/studio/dist/assets/node.browser-BISniYn8.js +1 -0
- package/studio/dist/assets/node.browser-HQ5SKBOf.js +1 -0
- package/studio/dist/assets/ollama-De5w84tG.js +1 -0
- package/studio/dist/assets/openai-compatible-Joy1oK3L.js +1 -0
- package/studio/dist/assets/ordinal-BDEzSJ7C.js +1 -0
- package/studio/dist/assets/packet-JDAUHWVQ-CDIMjHS_.js +1 -0
- package/studio/dist/assets/path-fybaL0A-.js +1 -0
- package/studio/dist/assets/pegDiagram-ECIUJNBX-DWXm4mwS.js +1 -0
- package/studio/dist/assets/pie-XZMESJXO-CY3QQiKZ.js +1 -0
- package/studio/dist/assets/pieDiagram-GNMPY74T-M1aCnI7W.js +39 -0
- package/studio/dist/assets/python.worker-CzTrFdd9.js +24 -0
- package/studio/dist/assets/quadrantDiagram-D57K4MC3-BFFtO7U5.js +7 -0
- package/studio/dist/assets/radar-ABXABTNO-DT0W4_Xn.js +1 -0
- package/studio/dist/assets/railroad-I3PHUGI6-_VCpgZR5.js +1 -0
- package/studio/dist/assets/railroad-abnf-LNEFI6M7-DkSpQ5YY.js +1 -0
- package/studio/dist/assets/railroad-ebnf-DWYD2UWJ-BqWILdGb.js +1 -0
- package/studio/dist/assets/railroad-peg-JR7BVNR7-BB0bQL-Z.js +1 -0
- package/studio/dist/assets/railroadDiagram-Y72NRBL7-CjLKjIdJ.js +1 -0
- package/studio/dist/assets/react-B8SKf8RM.js +1 -0
- package/studio/dist/assets/rehype-katex-C-sKddsH.js +259 -0
- package/studio/dist/assets/remark-math-C-ox67hE.js +5 -0
- package/studio/dist/assets/requirementDiagram-EQYSW5RA-CnwzZqR8.js +84 -0
- package/studio/dist/assets/rolldown-runtime-COiEfhsA.js +1 -0
- package/studio/dist/assets/rough.esm-Dy-Kn_BL.js +1 -0
- package/studio/dist/assets/sankeyDiagram-SY24JHO5-CPDugTUl.js +40 -0
- package/studio/dist/assets/schemas-BOc2YsOP.js +55 -0
- package/studio/dist/assets/sequenceDiagram-BLEHNHQU-1dLfeU9Y.js +169 -0
- package/studio/dist/assets/settings-DhwuyNPU.js +1 -0
- package/studio/dist/assets/sizeCapture-INFHLROL-3uHXrFqA.js +1 -0
- package/studio/dist/assets/spec-nAnsSMkw.js +1 -0
- package/studio/dist/assets/sql-wasm-C1D_RJuy.wasm +0 -0
- package/studio/dist/assets/sql.worker-D7pBHOrq.js +4 -0
- package/studio/dist/assets/src-BWF7jMDv.js +1 -0
- package/studio/dist/assets/stateDiagram-v2-A6IESKWZ-DR7CmEjj.js +291 -0
- package/studio/dist/assets/swimlanes-B3ENRKFQ-BAWB9omQ.js +1 -0
- package/studio/dist/assets/swimlanesDiagram-AYQRV4AK-qeuHru16.js +8 -0
- package/studio/dist/assets/timeline-definition-JXYKGLGB-DNkj8z77.js +120 -0
- package/studio/dist/assets/treeView-D4JQ5SDB-BA-ouhMb.js +1 -0
- package/studio/dist/assets/treemap-SAJKECNS-BrrGxsAb.js +1 -0
- package/studio/dist/assets/useStream-rdsR6A7P.js +1 -0
- package/studio/dist/assets/usecaseDiagram-WPOLBNNX-BB3D0Dt_.js +328 -0
- package/studio/dist/assets/vennDiagram-Q3Y3IDCW-BrtqtKTE.js +34 -0
- package/studio/dist/assets/wardley-7MLQ67FV-BKkemDql.js +1 -0
- package/studio/dist/assets/wardleyDiagram-IVAIDKWA-DFnI3P5z.js +78 -0
- package/studio/dist/assets/xychartDiagram-4URNHDU4-Da0DZG_q.js +7 -0
- package/studio/dist/index.html +20 -0
- package/studio/dist/preview.html +22 -0
- package/studio/migrations/0001_init.sql +188 -0
- package/studio/migrations/0002_external_ids.sql +20 -0
- package/studio/migrations/0003_course_language.sql +12 -0
- package/studio/migrations/0004_capstone.sql +51 -0
- package/studio/migrations/0005_beta_loop.sql +73 -0
- package/studio/migrations/0006_interactive_exercises.sql +49 -0
- package/studio/src/lib/activity.js +92 -0
- package/studio/src/lib/ai/anthropic.js +109 -0
- package/studio/src/lib/ai/bridge.js +126 -0
- package/studio/src/lib/ai/catalog.js +160 -0
- package/studio/src/lib/ai/config.js +70 -0
- package/studio/src/lib/ai/errors.js +95 -0
- package/studio/src/lib/ai/fetch-local.js +104 -0
- package/studio/src/lib/ai/index.js +194 -0
- package/studio/src/lib/ai/ollama.js +136 -0
- package/studio/src/lib/ai/openai-compatible.js +101 -0
- package/studio/src/lib/ai/provider-utils.js +107 -0
- package/studio/src/lib/ai/sse.js +99 -0
- package/studio/src/lib/ai/status.js +92 -0
- package/studio/src/lib/ai/useStream.js +109 -0
- package/studio/src/lib/beta/metrics.js +147 -0
- package/studio/src/lib/beta/report.js +134 -0
- package/studio/src/lib/capstone-evidence.js +758 -0
- package/studio/src/lib/capstone.js +663 -0
- package/studio/src/lib/chat-store.js +128 -0
- package/studio/src/lib/course-export.js +38 -0
- package/studio/src/lib/course.js +187 -0
- package/studio/src/lib/exercises/journal-entry.js +259 -0
- package/studio/src/lib/exercises/placement.js +31 -0
- package/studio/src/lib/exercises/registry.js +19 -0
- package/studio/src/lib/exercises/spec.js +129 -0
- package/studio/src/lib/i18n/detect.js +162 -0
- package/studio/src/lib/i18n/en/app.json +23 -0
- package/studio/src/lib/i18n/en/betaReport.json +55 -0
- package/studio/src/lib/i18n/en/capstone.json +57 -0
- package/studio/src/lib/i18n/en/common.json +25 -0
- package/studio/src/lib/i18n/en/course.json +27 -0
- package/studio/src/lib/i18n/en/data.json +35 -0
- package/studio/src/lib/i18n/en/durability.json +5 -0
- package/studio/src/lib/i18n/en/errors.json +20 -0
- package/studio/src/lib/i18n/en/exercises.json +55 -0
- package/studio/src/lib/i18n/en/externalEvidence.json +76 -0
- package/studio/src/lib/i18n/en/home.json +18 -0
- package/studio/src/lib/i18n/en/lesson.json +33 -0
- package/studio/src/lib/i18n/en/nav.json +8 -0
- package/studio/src/lib/i18n/en/newCourse.json +51 -0
- package/studio/src/lib/i18n/en/onboarding.json +35 -0
- package/studio/src/lib/i18n/en/playwrightTemplate.json +20 -0
- package/studio/src/lib/i18n/en/practice.json +79 -0
- package/studio/src/lib/i18n/en/provider.json +27 -0
- package/studio/src/lib/i18n/en/providerMeta.json +26 -0
- package/studio/src/lib/i18n/en/reflection.json +17 -0
- package/studio/src/lib/i18n/en/revision.json +52 -0
- package/studio/src/lib/i18n/en/runnerNotice.json +14 -0
- package/studio/src/lib/i18n/en/settings.json +12 -0
- package/studio/src/lib/i18n/en/storage.json +49 -0
- package/studio/src/lib/i18n/en/tutor.json +39 -0
- package/studio/src/lib/i18n/en/workspace.json +50 -0
- package/studio/src/lib/i18n/index.js +144 -0
- package/studio/src/lib/i18n/languages.js +93 -0
- package/studio/src/lib/i18n/vi/app.json +23 -0
- package/studio/src/lib/i18n/vi/betaReport.json +55 -0
- package/studio/src/lib/i18n/vi/capstone.json +57 -0
- package/studio/src/lib/i18n/vi/common.json +25 -0
- package/studio/src/lib/i18n/vi/course.json +27 -0
- package/studio/src/lib/i18n/vi/data.json +35 -0
- package/studio/src/lib/i18n/vi/durability.json +5 -0
- package/studio/src/lib/i18n/vi/errors.json +20 -0
- package/studio/src/lib/i18n/vi/exercises.json +55 -0
- package/studio/src/lib/i18n/vi/externalEvidence.json +76 -0
- package/studio/src/lib/i18n/vi/home.json +18 -0
- package/studio/src/lib/i18n/vi/lesson.json +33 -0
- package/studio/src/lib/i18n/vi/nav.json +8 -0
- package/studio/src/lib/i18n/vi/newCourse.json +51 -0
- package/studio/src/lib/i18n/vi/onboarding.json +35 -0
- package/studio/src/lib/i18n/vi/playwrightTemplate.json +20 -0
- package/studio/src/lib/i18n/vi/practice.json +79 -0
- package/studio/src/lib/i18n/vi/provider.json +27 -0
- package/studio/src/lib/i18n/vi/providerMeta.json +26 -0
- package/studio/src/lib/i18n/vi/reflection.json +17 -0
- package/studio/src/lib/i18n/vi/revision.json +52 -0
- package/studio/src/lib/i18n/vi/runnerNotice.json +14 -0
- package/studio/src/lib/i18n/vi/settings.json +12 -0
- package/studio/src/lib/i18n/vi/storage.json +49 -0
- package/studio/src/lib/i18n/vi/tutor.json +39 -0
- package/studio/src/lib/i18n/vi/workspace.json +50 -0
- package/studio/src/lib/ids.js +10 -0
- package/studio/src/lib/json.js +26 -0
- package/studio/src/lib/markdown.js +74 -0
- package/studio/src/lib/prompts/base.js +139 -0
- package/studio/src/lib/prompts/capstone.js +777 -0
- package/studio/src/lib/prompts/course.js +150 -0
- package/studio/src/lib/prompts/lesson.js +275 -0
- package/studio/src/lib/prompts/practice.js +511 -0
- package/studio/src/lib/prompts/tutor.js +509 -0
- package/studio/src/lib/repo/blocked-adapter.js +69 -0
- package/studio/src/lib/repo/events.js +239 -0
- package/studio/src/lib/repo/idb-adapter.js +226 -0
- package/studio/src/lib/repo/index.js +1315 -0
- package/studio/src/lib/repo/memory-adapter.js +115 -0
- package/studio/src/lib/repo/remote-adapter.js +158 -0
- package/studio/src/lib/repo/schema.js +143 -0
- package/studio/src/lib/repo/select-adapter.js +345 -0
- package/studio/src/lib/router.js +71 -0
- package/studio/src/lib/runner/browser.js +244 -0
- package/studio/src/lib/runner/checks.js +248 -0
- package/studio/src/lib/runner/current-code.js +98 -0
- package/studio/src/lib/runner/esm-cjs.js +109 -0
- package/studio/src/lib/runner/hash.js +34 -0
- package/studio/src/lib/runner/history.js +48 -0
- package/studio/src/lib/runner/index.js +168 -0
- package/studio/src/lib/runner/languages.js +258 -0
- package/studio/src/lib/runner/local.js +139 -0
- package/studio/src/lib/runner/path.js +104 -0
- package/studio/src/lib/runner/skeleton.js +315 -0
- package/studio/src/lib/runner/strip-types.js +134 -0
- package/studio/src/lib/settings.js +399 -0
- package/studio/src/lib/srs.js +39 -0
- package/templates/agent/settings.json +6 -0
- package/templates/claude/plugin.json +139 -0
- package/templates/claude/settings.json +15 -0
- package/templates/cursor/professor.mdc +4 -0
- package/templates/cursor/settings.json +18 -0
- package/templates/gemini/GEMINI.md +28 -0
- package/templates/gemini/settings.json +5 -0
- package/templates/opencode/opencode.json +3 -0
- package/templates/researcher/README.md +61 -0
- package/templates/researcher/settings.json +18 -0
- package/web/client/components.json +15 -0
- package/web/client/index.html +12 -0
- package/web/client/package-lock.json +8732 -0
- package/web/client/package.json +39 -0
- package/web/client/postcss.config.js +6 -0
- package/web/client/src/App.jsx +140 -0
- package/web/client/src/api/client.js +357 -0
- package/web/client/src/components/ChatPanel.jsx +417 -0
- package/web/client/src/components/CommandPills.jsx +50 -0
- package/web/client/src/components/DeprecationBanner.jsx +21 -0
- package/web/client/src/components/ExerciseEditor.jsx +164 -0
- package/web/client/src/components/Header.jsx +119 -0
- package/web/client/src/components/LecturePanel.jsx +342 -0
- package/web/client/src/components/MobileTabSwitcher.jsx +51 -0
- package/web/client/src/components/SplitPane.jsx +42 -0
- package/web/client/src/components/ui/alert.jsx +38 -0
- package/web/client/src/components/ui/badge.jsx +24 -0
- package/web/client/src/components/ui/button.jsx +23 -0
- package/web/client/src/components/ui/select.jsx +74 -0
- package/web/client/src/hooks/useSSE.js +152 -0
- package/web/client/src/hooks/useWebSocket.js +98 -0
- package/web/client/src/index.css +23 -0
- package/web/client/src/lib/utils.js +6 -0
- package/web/client/src/main.jsx +12 -0
- package/web/client/src/utils/markdown.js +50 -0
- package/web/client/tailwind.config.js +8 -0
- package/web/client/vite.config.js +21 -0
- package/web/dist/assets/index-C1W0eXIQ.js +294 -0
- package/web/dist/assets/index-C3OUx70T.css +1 -0
- package/web/dist/index.html +13 -0
- package/web/lecture-archive.js +172 -0
- package/web/legacy-server.js +1277 -0
- package/web/package-lock.json +855 -0
- package/web/package.json +10 -0
- package/web/security.js +1016 -0
- package/web/server.js +390 -0
- package/web/studio-api/agents.js +580 -0
- package/web/studio-api/bridge.js +199 -0
- package/web/studio-api/db.js +66 -0
- package/web/studio-api/guards.js +201 -0
- package/web/studio-api/health.js +538 -0
- package/web/studio-api.js +214 -0
- package/web/studio-db.js +380 -0
- package/web/studio-runner.js +568 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory StorageAdapter. Used directly in tests and as the fallback when IndexedDB is
|
|
3
|
+
* unavailable (private mode, some embedded webviews, vitest without fake-indexeddb).
|
|
4
|
+
*
|
|
5
|
+
* Contract (see remote-adapter.js): dumb table-level storage. No stamping, no business logic.
|
|
6
|
+
*/
|
|
7
|
+
import { TABLE_NAMES, SCHEMA_VERSION, tableSpec, assertIndexedField } from './schema.js';
|
|
8
|
+
|
|
9
|
+
export const APP_ID = 'professor-studio';
|
|
10
|
+
|
|
11
|
+
function applyList(rows, { where, includeDeleted = false, orderBy, limit } = {}) {
|
|
12
|
+
let out = rows;
|
|
13
|
+
if (where) {
|
|
14
|
+
for (const key of Object.keys(where)) {
|
|
15
|
+
out = out.filter((r) => r[key] === where[key]);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (!includeDeleted) out = out.filter((r) => !r.deleted_at);
|
|
19
|
+
if (orderBy) {
|
|
20
|
+
const field = typeof orderBy === 'string' ? orderBy : orderBy.field;
|
|
21
|
+
const dir = typeof orderBy === 'string' ? 1 : orderBy.dir === 'desc' ? -1 : 1;
|
|
22
|
+
out = [...out].sort((a, b) => {
|
|
23
|
+
const av = a[field];
|
|
24
|
+
const bv = b[field];
|
|
25
|
+
if (av === bv) return 0;
|
|
26
|
+
return (av > bv ? 1 : -1) * dir;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
if (typeof limit === 'number') out = out.slice(0, limit);
|
|
30
|
+
return out.map((r) => ({ ...r }));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @returns {import('./remote-adapter.js').StorageAdapter & { label: string }}
|
|
35
|
+
*/
|
|
36
|
+
export function createMemoryAdapter() {
|
|
37
|
+
/** @type {Record<string, Map<string, object>>} */
|
|
38
|
+
const stores = Object.fromEntries(TABLE_NAMES.map((t) => [t, new Map()]));
|
|
39
|
+
|
|
40
|
+
function store(table) {
|
|
41
|
+
const s = stores[table];
|
|
42
|
+
if (!s) throw new Error(`Unknown table: ${table}`);
|
|
43
|
+
return s;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
label: 'memory',
|
|
48
|
+
async get(table, id) {
|
|
49
|
+
const row = store(table).get(id);
|
|
50
|
+
return row ? { ...row } : null;
|
|
51
|
+
},
|
|
52
|
+
async put(table, row) {
|
|
53
|
+
const spec = tableSpec(table);
|
|
54
|
+
const id = row[spec.id];
|
|
55
|
+
if (id === undefined || id === null) throw new Error(`Row for "${table}" is missing "${spec.id}"`);
|
|
56
|
+
const next = { ...row, [spec.id]: id };
|
|
57
|
+
store(table).set(id, next);
|
|
58
|
+
return { ...next };
|
|
59
|
+
},
|
|
60
|
+
async putMany(table, rows) {
|
|
61
|
+
const out = [];
|
|
62
|
+
for (const row of rows || []) out.push(await this.put(table, row));
|
|
63
|
+
return out;
|
|
64
|
+
},
|
|
65
|
+
async list(table, opts = {}) {
|
|
66
|
+
if (opts.where) {
|
|
67
|
+
for (const field of Object.keys(opts.where)) assertIndexedField(table, field);
|
|
68
|
+
}
|
|
69
|
+
return applyList([...store(table).values()], opts);
|
|
70
|
+
},
|
|
71
|
+
async softDelete(table, id) {
|
|
72
|
+
const s = store(table);
|
|
73
|
+
const row = s.get(id);
|
|
74
|
+
if (!row) return null;
|
|
75
|
+
const next = { ...row, deleted_at: new Date().toISOString() };
|
|
76
|
+
s.set(id, next);
|
|
77
|
+
return { ...next };
|
|
78
|
+
},
|
|
79
|
+
async exportAll() {
|
|
80
|
+
const tables = {};
|
|
81
|
+
for (const t of TABLE_NAMES) tables[t] = [...stores[t].values()].map((r) => ({ ...r }));
|
|
82
|
+
return {
|
|
83
|
+
app: APP_ID,
|
|
84
|
+
version: 1,
|
|
85
|
+
schemaVersion: SCHEMA_VERSION,
|
|
86
|
+
exportedAt: new Date().toISOString(),
|
|
87
|
+
tables,
|
|
88
|
+
};
|
|
89
|
+
},
|
|
90
|
+
async importAll(dump) {
|
|
91
|
+
validateDump(dump);
|
|
92
|
+
for (const t of TABLE_NAMES) {
|
|
93
|
+
const s = stores[t];
|
|
94
|
+
for (const row of dump.tables[t] || []) {
|
|
95
|
+
const spec = tableSpec(t);
|
|
96
|
+
s.set(row[spec.id], { ...row });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return { imported: TABLE_NAMES.reduce((n, t) => n + (dump.tables[t]?.length || 0), 0) };
|
|
100
|
+
},
|
|
101
|
+
async info() {
|
|
102
|
+
return { kind: 'memory', persistent: false };
|
|
103
|
+
},
|
|
104
|
+
async clearAll() {
|
|
105
|
+
for (const t of TABLE_NAMES) stores[t].clear();
|
|
106
|
+
return { cleared: true };
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** @param {any} dump */
|
|
112
|
+
export function validateDump(dump) {
|
|
113
|
+
if (!dump || dump.app !== APP_ID) throw new Error('Not a Professor Studio backup file');
|
|
114
|
+
if (!dump.tables || typeof dump.tables !== 'object') throw new Error('Backup file has no tables');
|
|
115
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RemoteAdapter — talks to the local Studio server (`web/studio-api.js`) over HTTP, and to the
|
|
3
|
+
* future Cloudflare Worker + D1 backend unchanged (same contract, only base URL + token differ).
|
|
4
|
+
*
|
|
5
|
+
* The adapter is a dumb, table-level store: no business logic, no stamping. `repo/index.js` owns
|
|
6
|
+
* ids, owner_id, timestamps and tombstones. See the contract documented below.
|
|
7
|
+
*
|
|
8
|
+
* --- StorageAdapter contract (same as idb- and memory-adapter) ------------------------------
|
|
9
|
+
* get(table, id) -> row | null
|
|
10
|
+
* put(table, row) -> row (upsert by row.id; adapter stamps nothing)
|
|
11
|
+
* putMany(table, rows) -> rows
|
|
12
|
+
* list(table, { where?, includeDeleted?, orderBy?, limit? }) -> rows
|
|
13
|
+
* where: equality on indexed fields only (schema.js)
|
|
14
|
+
* orderBy: field name | { field, dir: 'asc'|'desc' }
|
|
15
|
+
* softDelete(table, id) -> row | null (sets deleted_at = now, ISO-8601)
|
|
16
|
+
* exportAll() -> { app, version, schemaVersion, exportedAt, tables }
|
|
17
|
+
* importAll(dump) -> { imported }
|
|
18
|
+
* clearAll() -> { cleared: true } (removes every row — STORE-03)
|
|
19
|
+
* info() -> { kind: 'indexeddb'|'memory'|'remote', persistent, … }
|
|
20
|
+
* -------------------------------------------------------------------------------------------
|
|
21
|
+
*
|
|
22
|
+
* --- HTTP contract (implemented by web/studio-api.js; the D1 Worker reuses it) --------------
|
|
23
|
+
* GET {base}/api/studio/db/health header X-Studio-Token
|
|
24
|
+
* -> { ok: true, kind: 'sqlite', path, schemaVersion }
|
|
25
|
+
* POST {base}/api/studio/db header X-Studio-Token
|
|
26
|
+
* body { op, table?, args: [ ...positional args ] }
|
|
27
|
+
* -> { ok: true, result }
|
|
28
|
+
* | { ok: false, error: '<message>', code: '<code>' }
|
|
29
|
+
* -------------------------------------------------------------------------------------------
|
|
30
|
+
*/
|
|
31
|
+
import { tableSpec, assertIndexedField } from './schema.js';
|
|
32
|
+
import { isLoopbackBridgeUrl } from '../settings.js';
|
|
33
|
+
import { providerError } from '../ai/errors.js';
|
|
34
|
+
|
|
35
|
+
/** Raised when the Studio server rejects a request (used so callers can distinguish from network). */
|
|
36
|
+
export class RemoteAdapterError extends Error {
|
|
37
|
+
/** @param {string} message @param {{ code?: string, status?: number }} [info] */
|
|
38
|
+
constructor(message, { code = 'remote_error', status = 0 } = {}) {
|
|
39
|
+
super(message);
|
|
40
|
+
this.name = 'RemoteAdapterError';
|
|
41
|
+
this.code = code;
|
|
42
|
+
this.status = status;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param {{ baseUrl: string, token?: string, fetchImpl?: typeof fetch }} opts
|
|
48
|
+
* @returns {import('./remote-adapter.js').StorageAdapter & { label: string, health: () => Promise<any> }}
|
|
49
|
+
*/
|
|
50
|
+
export function createRemoteAdapter({ baseUrl, token, fetchImpl = globalThis.fetch } = {}) {
|
|
51
|
+
const root = String(baseUrl || '').replace(/\/$/, '');
|
|
52
|
+
const headers = {
|
|
53
|
+
'Content-Type': 'application/json',
|
|
54
|
+
...(token ? { 'X-Studio-Token': token } : {}),
|
|
55
|
+
};
|
|
56
|
+
/** @type {{ path?: string, schemaVersion?: number }|null} */
|
|
57
|
+
let cachedHealth = null;
|
|
58
|
+
|
|
59
|
+
function assertLocalEndpoint() {
|
|
60
|
+
if (!isLoopbackBridgeUrl(root)) throw providerError('loopback_required');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function post(op, table, args = []) {
|
|
64
|
+
assertLocalEndpoint();
|
|
65
|
+
let res;
|
|
66
|
+
try {
|
|
67
|
+
res = await fetchImpl(`${root}/api/studio/db`, {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers,
|
|
70
|
+
body: JSON.stringify(table ? { op, table, args } : { op, args }),
|
|
71
|
+
});
|
|
72
|
+
} catch (err) {
|
|
73
|
+
throw new RemoteAdapterError(`Studio server not reachable at ${root}: ${err.message}`, { code: 'network' });
|
|
74
|
+
}
|
|
75
|
+
if (res.status === 401 || res.status === 403) {
|
|
76
|
+
throw new RemoteAdapterError(`Studio server rejected the pairing token (${res.status})`, {
|
|
77
|
+
code: 'auth',
|
|
78
|
+
status: res.status,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (!res.ok) {
|
|
82
|
+
let payload = null;
|
|
83
|
+
try {
|
|
84
|
+
payload = await res.json();
|
|
85
|
+
} catch {
|
|
86
|
+
/* not JSON */
|
|
87
|
+
}
|
|
88
|
+
throw new RemoteAdapterError(payload?.error || `Studio server error ${res.status}`, {
|
|
89
|
+
code: payload?.code || 'server',
|
|
90
|
+
status: res.status,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
const json = await res.json();
|
|
94
|
+
if (!json.ok) throw new RemoteAdapterError(json.error || 'Studio server error', { code: json.code || 'db_error' });
|
|
95
|
+
return json.result;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function health() {
|
|
99
|
+
assertLocalEndpoint();
|
|
100
|
+
let res;
|
|
101
|
+
try {
|
|
102
|
+
res = await fetchImpl(`${root}/api/studio/db/health`, { headers });
|
|
103
|
+
} catch (err) {
|
|
104
|
+
throw new RemoteAdapterError(`Studio server not reachable at ${root}: ${err.message}`, { code: 'network' });
|
|
105
|
+
}
|
|
106
|
+
if (res.status === 401 || res.status === 403) {
|
|
107
|
+
throw new RemoteAdapterError(`Studio server rejected the pairing token (${res.status})`, { code: 'auth', status: res.status });
|
|
108
|
+
}
|
|
109
|
+
if (!res.ok) throw new RemoteAdapterError(`Studio server error ${res.status}`, { code: 'server', status: res.status });
|
|
110
|
+
const json = await res.json();
|
|
111
|
+
if (!json.ok) throw new RemoteAdapterError(json.error || 'Studio server error', { code: json.code || 'db_error' });
|
|
112
|
+
cachedHealth = { path: json.path, schemaVersion: json.schemaVersion };
|
|
113
|
+
return json;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
label: 'remote',
|
|
118
|
+
async get(table, id) {
|
|
119
|
+
tableSpec(table);
|
|
120
|
+
return post('get', table, [id]);
|
|
121
|
+
},
|
|
122
|
+
async put(table, row) {
|
|
123
|
+
tableSpec(table);
|
|
124
|
+
return post('put', table, [row]);
|
|
125
|
+
},
|
|
126
|
+
async putMany(table, rows) {
|
|
127
|
+
tableSpec(table);
|
|
128
|
+
return post('putMany', table, [rows]);
|
|
129
|
+
},
|
|
130
|
+
async list(table, opts = {}) {
|
|
131
|
+
tableSpec(table);
|
|
132
|
+
if (opts.where) for (const field of Object.keys(opts.where)) assertIndexedField(table, field);
|
|
133
|
+
return post('list', table, [opts]);
|
|
134
|
+
},
|
|
135
|
+
async softDelete(table, id) {
|
|
136
|
+
tableSpec(table);
|
|
137
|
+
return post('softDelete', table, [id]);
|
|
138
|
+
},
|
|
139
|
+
async exportAll() {
|
|
140
|
+
return post('exportAll');
|
|
141
|
+
},
|
|
142
|
+
async importAll(dump) {
|
|
143
|
+
return post('importAll', null, [dump]);
|
|
144
|
+
},
|
|
145
|
+
async clearAll() {
|
|
146
|
+
return post('clearAll');
|
|
147
|
+
},
|
|
148
|
+
async info() {
|
|
149
|
+
try {
|
|
150
|
+
const h = cachedHealth || (await health());
|
|
151
|
+
return { kind: 'remote', persistent: true, path: h.path, schemaVersion: h.schemaVersion };
|
|
152
|
+
} catch {
|
|
153
|
+
return { kind: 'remote', persistent: true };
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
health,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JS metadata mirroring migrations/0001_init.sql.
|
|
3
|
+
*
|
|
4
|
+
* The IndexedDB adapter creates one object store per table and one index per entry in
|
|
5
|
+
* `indexes`. Adapters only guarantee equality filtering on these fields (see StorageAdapter
|
|
6
|
+
* contract in remote-adapter.js) — richer queries live in repo/index.js so the SQLite/D1
|
|
7
|
+
* adapters stay generic.
|
|
8
|
+
*
|
|
9
|
+
* `fields` lists every column (documentation / the SQLite side); `indexes` are the only fields
|
|
10
|
+
* an adapter may use in `list({ where })`. `quiz_seed` is a demo-only convenience column
|
|
11
|
+
* (pre-written quiz so the bundled demo course is walkable with no provider).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** @typedef {{ id: string, fields: string[], indexes: string[] }} TableSpec */
|
|
15
|
+
|
|
16
|
+
/** @type {Record<string, TableSpec>} */
|
|
17
|
+
export const TABLES = {
|
|
18
|
+
courses: {
|
|
19
|
+
id: 'id',
|
|
20
|
+
fields: ['id', 'owner_id', 'title', 'description', 'level', 'kind', 'goal', 'topics_json', 'source', 'status', 'language', 'external_id', 'created_at', 'updated_at', 'deleted_at'],
|
|
21
|
+
indexes: ['external_id'],
|
|
22
|
+
},
|
|
23
|
+
lessons: {
|
|
24
|
+
id: 'id',
|
|
25
|
+
fields: ['id', 'owner_id', 'course_id', 'position', 'title', 'summary', 'status', 'completed_at', 'current_version_id', 'quiz_seed', 'external_id', 'created_at', 'updated_at', 'deleted_at'],
|
|
26
|
+
indexes: ['course_id', 'status', 'external_id'],
|
|
27
|
+
},
|
|
28
|
+
lesson_versions: {
|
|
29
|
+
id: 'id',
|
|
30
|
+
fields: ['id', 'owner_id', 'lesson_id', 'version', 'content_md', 'model', 'provider', 'external_id', 'created_at', 'updated_at', 'deleted_at'],
|
|
31
|
+
indexes: ['lesson_id', 'external_id'],
|
|
32
|
+
},
|
|
33
|
+
quiz_attempts: {
|
|
34
|
+
id: 'id',
|
|
35
|
+
fields: ['id', 'owner_id', 'lesson_id', 'questions_json', 'answers_json', 'score', 'total', 'created_at', 'updated_at', 'deleted_at'],
|
|
36
|
+
indexes: ['lesson_id'],
|
|
37
|
+
},
|
|
38
|
+
cards: {
|
|
39
|
+
id: 'id',
|
|
40
|
+
fields: ['id', 'owner_id', 'course_id', 'lesson_id', 'front', 'back', 'ease', 'interval_days', 'reps', 'lapses', 'due_at', 'external_id', 'created_at', 'updated_at', 'deleted_at'],
|
|
41
|
+
indexes: ['course_id', 'lesson_id', 'due_at', 'external_id'],
|
|
42
|
+
},
|
|
43
|
+
reviews: {
|
|
44
|
+
id: 'id',
|
|
45
|
+
fields: ['id', 'owner_id', 'card_id', 'grade', 'reviewed_at', 'created_at', 'updated_at', 'deleted_at'],
|
|
46
|
+
indexes: ['card_id'],
|
|
47
|
+
},
|
|
48
|
+
chat_messages: {
|
|
49
|
+
id: 'id',
|
|
50
|
+
fields: ['id', 'owner_id', 'lesson_id', 'role', 'content', 'mode', 'created_at', 'updated_at', 'deleted_at'],
|
|
51
|
+
indexes: ['lesson_id'],
|
|
52
|
+
},
|
|
53
|
+
reflections: {
|
|
54
|
+
id: 'id',
|
|
55
|
+
fields: ['id', 'owner_id', 'lesson_id', 'hardest', 'confidence', 'note', 'created_at', 'updated_at', 'deleted_at'],
|
|
56
|
+
indexes: ['lesson_id'],
|
|
57
|
+
},
|
|
58
|
+
notes: {
|
|
59
|
+
id: 'id',
|
|
60
|
+
fields: ['id', 'owner_id', 'course_id', 'lesson_id', 'content_md', 'created_at', 'updated_at', 'deleted_at'],
|
|
61
|
+
indexes: ['course_id', 'lesson_id'],
|
|
62
|
+
},
|
|
63
|
+
activity: {
|
|
64
|
+
id: 'id',
|
|
65
|
+
fields: ['id', 'owner_id', 'day', 'kind', 'ref_id', 'created_at', 'updated_at', 'deleted_at'],
|
|
66
|
+
indexes: ['day', 'kind'],
|
|
67
|
+
},
|
|
68
|
+
workspace_files: {
|
|
69
|
+
id: 'id',
|
|
70
|
+
fields: ['id', 'owner_id', 'course_id', 'lesson_id', 'path', 'language', 'content', 'created_at', 'updated_at', 'deleted_at'],
|
|
71
|
+
indexes: ['lesson_id'],
|
|
72
|
+
},
|
|
73
|
+
run_results: {
|
|
74
|
+
id: 'id',
|
|
75
|
+
fields: ['id', 'owner_id', 'lesson_id', 'file_id', 'runner', 'language', 'stdout', 'stderr', 'exit_code', 'checks_json', 'duration_ms', 'created_at', 'updated_at', 'deleted_at'],
|
|
76
|
+
indexes: ['lesson_id'],
|
|
77
|
+
},
|
|
78
|
+
exercises: {
|
|
79
|
+
id: 'id',
|
|
80
|
+
fields: ['id', 'owner_id', 'course_id', 'lesson_id', 'position', 'type', 'spec_json', 'external_id', 'created_at', 'updated_at', 'deleted_at'],
|
|
81
|
+
indexes: ['course_id', 'lesson_id', 'external_id'],
|
|
82
|
+
},
|
|
83
|
+
exercise_attempts: {
|
|
84
|
+
id: 'id',
|
|
85
|
+
fields: ['id', 'owner_id', 'course_id', 'lesson_id', 'exercise_id', 'rows_json', 'result_json', 'correct', 'created_at', 'updated_at', 'deleted_at'],
|
|
86
|
+
indexes: ['exercise_id', 'lesson_id', 'course_id'],
|
|
87
|
+
},
|
|
88
|
+
capstones: {
|
|
89
|
+
id: 'id',
|
|
90
|
+
fields: ['id', 'owner_id', 'course_id', 'kind', 'brief_md', 'rubric_json', 'tests_json', 'hidden_answers_json', 'artifact_json', 'external_id', 'created_at', 'updated_at', 'deleted_at'],
|
|
91
|
+
indexes: ['course_id', 'external_id'],
|
|
92
|
+
},
|
|
93
|
+
capstone_submissions: {
|
|
94
|
+
id: 'id',
|
|
95
|
+
fields: ['id', 'owner_id', 'capstone_id', 'attempt', 'content_md', 'repo_url', 'files_json', 'test_results_json', 'review_json', 'verdict', 'external_id', 'created_at', 'updated_at', 'deleted_at'],
|
|
96
|
+
indexes: ['capstone_id', 'external_id'],
|
|
97
|
+
},
|
|
98
|
+
learning_events: {
|
|
99
|
+
id: 'id',
|
|
100
|
+
fields: ['id', 'owner_id', 'day', 'kind', 'surface', 'course_id', 'lesson_id', 'ref_id', 'meta_json', 'created_at', 'updated_at', 'deleted_at'],
|
|
101
|
+
indexes: ['day', 'kind', 'surface', 'course_id', 'lesson_id'],
|
|
102
|
+
},
|
|
103
|
+
revision_links: {
|
|
104
|
+
id: 'id',
|
|
105
|
+
fields: ['id', 'owner_id', 'surface', 'course_id', 'lesson_id', 'attempt_table', 'attempt_id', 'feedback_table', 'feedback_id', 'revised_table', 'revised_id', 'link_kind', 'created_at', 'updated_at', 'deleted_at'],
|
|
106
|
+
indexes: ['surface', 'course_id', 'lesson_id', 'attempt_id', 'revised_id', 'link_kind'],
|
|
107
|
+
},
|
|
108
|
+
beta_profile: {
|
|
109
|
+
id: 'id',
|
|
110
|
+
fields: ['id', 'owner_id', 'participant_id', 'activated_at', 'activated_day', 'created_at', 'updated_at', 'deleted_at'],
|
|
111
|
+
indexes: [],
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export const TABLE_NAMES = Object.keys(TABLES);
|
|
116
|
+
|
|
117
|
+
/** Highest migration file shipped in `studio/migrations/` (D-12 single constant source). */
|
|
118
|
+
export const LATEST_MIGRATION_VERSION = 6;
|
|
119
|
+
|
|
120
|
+
export const SCHEMA_VERSION = LATEST_MIGRATION_VERSION;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @param {string} table
|
|
124
|
+
* @returns {TableSpec}
|
|
125
|
+
*/
|
|
126
|
+
export function tableSpec(table) {
|
|
127
|
+
const spec = TABLES[table];
|
|
128
|
+
if (!spec) throw new Error(`Unknown table: ${table}`);
|
|
129
|
+
return spec;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Throw unless `field` is an equality-filterable (indexed) field of `table`.
|
|
134
|
+
* @param {string} table
|
|
135
|
+
* @param {string} field
|
|
136
|
+
*/
|
|
137
|
+
export function assertIndexedField(table, field) {
|
|
138
|
+
const spec = tableSpec(table);
|
|
139
|
+
if (field === spec.id) return;
|
|
140
|
+
if (!spec.indexes.includes(field)) {
|
|
141
|
+
throw new Error(`Field "${field}" is not indexed on table "${table}"`);
|
|
142
|
+
}
|
|
143
|
+
}
|