hitloop 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/LICENSE +21 -0
- package/README.md +218 -2
- package/dist/agent.js +318 -0
- package/dist/cli.js +897 -0
- package/dist/client/assets/app.css +1 -0
- package/dist/client/assets/chunks/abap-EINPUMCJ.js +1 -0
- package/dist/client/assets/chunks/abnfDiagram-S7VZAUFO-W2CM2W6D.js +1 -0
- package/dist/client/assets/chunks/actionscript-3-O776AQUO.js +1 -0
- package/dist/client/assets/chunks/ada-WLOTXRTV.js +1 -0
- package/dist/client/assets/chunks/ahk-EF37A2DY.js +1 -0
- package/dist/client/assets/chunks/ahk2-FH4K3CKT.js +1 -0
- package/dist/client/assets/chunks/angular-html-ZALQFV2H.js +1 -0
- package/dist/client/assets/chunks/angular-ts-4IWMF7QO.js +1 -0
- package/dist/client/assets/chunks/apache-44AYOEWM.js +1 -0
- package/dist/client/assets/chunks/apex-XRYPID42.js +1 -0
- package/dist/client/assets/chunks/apl-5HBALFOI.js +1 -0
- package/dist/client/assets/chunks/applescript-2DZEHZEC.js +1 -0
- package/dist/client/assets/chunks/ara-ZGZQBLXL.js +1 -0
- package/dist/client/assets/chunks/architecture-WOLXFQ4H-EJGENNRL.js +1 -0
- package/dist/client/assets/chunks/architectureDiagram-YGSQEFNW-2VHWSKO6.js +36 -0
- package/dist/client/assets/chunks/asciidoc-GSQAKCPO.js +1 -0
- package/dist/client/assets/chunks/asm-BFURM5BK.js +1 -0
- package/dist/client/assets/chunks/astro-VJORWDZJ.js +1 -0
- package/dist/client/assets/chunks/awk-BSHTWXXX.js +1 -0
- package/dist/client/assets/chunks/ballerina-HAOQJ4B2.js +1 -0
- package/dist/client/assets/chunks/bat-OSH45EOF.js +1 -0
- package/dist/client/assets/chunks/beancount-RBATIHOV.js +1 -0
- package/dist/client/assets/chunks/berry-CZUIOUM4.js +1 -0
- package/dist/client/assets/chunks/bibtex-KCL2UWHE.js +1 -0
- package/dist/client/assets/chunks/bicep-SZJZB3SG.js +1 -0
- package/dist/client/assets/chunks/bird2-XBF5YQQV.js +1 -0
- package/dist/client/assets/chunks/blade-Q24LEVFC.js +1 -0
- package/dist/client/assets/chunks/blockDiagram-APKKGZFW-53LXSOOT.js +140 -0
- package/dist/client/assets/chunks/bpmnIcons-APHXDYXF-FMBRQNTY.js +1 -0
- package/dist/client/assets/chunks/bsl-TI44NWFB.js +1 -0
- package/dist/client/assets/chunks/c-NLHY4Y4N.js +1 -0
- package/dist/client/assets/chunks/c3-BUTEM6PO.js +1 -0
- package/dist/client/assets/chunks/c4Diagram-KJ2TOQU5-O53G4GQH.js +38 -0
- package/dist/client/assets/chunks/cadence-JIRTEU64.js +1 -0
- package/dist/client/assets/chunks/cairo-3W6NVDNW.js +1 -0
- package/dist/client/assets/chunks/chapel-BPBJ2E7K.js +1 -0
- package/dist/client/assets/chunks/chunk-25KVF5ZE.js +1 -0
- package/dist/client/assets/chunks/chunk-2CJKSQOI.js +1 -0
- package/dist/client/assets/chunks/chunk-2QIJRTWR.js +1 -0
- package/dist/client/assets/chunks/chunk-2ZCRJZGF.js +2 -0
- package/dist/client/assets/chunks/chunk-3ILNDZNY.js +213 -0
- package/dist/client/assets/chunks/chunk-3M2OOQ7S.js +1 -0
- package/dist/client/assets/chunks/chunk-3M57NTNK.js +1 -0
- package/dist/client/assets/chunks/chunk-3MR6DCZJ.js +1 -0
- package/dist/client/assets/chunks/chunk-3WGHS7F7.js +1 -0
- package/dist/client/assets/chunks/chunk-4JK75V33.js +1 -0
- package/dist/client/assets/chunks/chunk-4NXNVEWK.js +1 -0
- package/dist/client/assets/chunks/chunk-4RNNBDFQ.js +1 -0
- package/dist/client/assets/chunks/chunk-4T4NVH7V.js +1 -0
- package/dist/client/assets/chunks/chunk-4TFFBC35.js +1 -0
- package/dist/client/assets/chunks/chunk-4WYBE67P.js +1 -0
- package/dist/client/assets/chunks/chunk-56ITEUFA.js +1 -0
- package/dist/client/assets/chunks/chunk-5DXRE7IH.js +1 -0
- package/dist/client/assets/chunks/chunk-5IMADCNU.js +1 -0
- package/dist/client/assets/chunks/chunk-5XZPGKN4.js +1 -0
- package/dist/client/assets/chunks/chunk-6O32UKSP.js +1 -0
- package/dist/client/assets/chunks/chunk-7BAEB64Q.js +1 -0
- package/dist/client/assets/chunks/chunk-7FMWBHZO.js +1 -0
- package/dist/client/assets/chunks/chunk-7GFDITUT.js +1 -0
- package/dist/client/assets/chunks/chunk-7H4MLBHI.js +1 -0
- package/dist/client/assets/chunks/chunk-7OJ4MEEB.js +1 -0
- package/dist/client/assets/chunks/chunk-7RSEISFE.js +1 -0
- package/dist/client/assets/chunks/chunk-7YNZZQVT.js +1 -0
- package/dist/client/assets/chunks/chunk-AB7TIL2G.js +1 -0
- package/dist/client/assets/chunks/chunk-ADDF76FL.js +1 -0
- package/dist/client/assets/chunks/chunk-AF6UTCD4.js +321 -0
- package/dist/client/assets/chunks/chunk-AJ2K6ASU.js +1 -0
- package/dist/client/assets/chunks/chunk-AUBAENQZ.js +1 -0
- package/dist/client/assets/chunks/chunk-BJEYVZ65.js +1 -0
- package/dist/client/assets/chunks/chunk-BLRM6LXA.js +1 -0
- package/dist/client/assets/chunks/chunk-BOCTYCDZ.js +1 -0
- package/dist/client/assets/chunks/chunk-BU4VRYN7.js +1 -0
- package/dist/client/assets/chunks/chunk-CALYHV7D.js +1 -0
- package/dist/client/assets/chunks/chunk-CF4W3RZ4.js +1 -0
- package/dist/client/assets/chunks/chunk-DCZ2OBSL.js +1 -0
- package/dist/client/assets/chunks/chunk-DUFPBTHD.js +1 -0
- package/dist/client/assets/chunks/chunk-DUICIQBC.js +88 -0
- package/dist/client/assets/chunks/chunk-EH2N475V.js +1 -0
- package/dist/client/assets/chunks/chunk-EHLLNPZT.js +1 -0
- package/dist/client/assets/chunks/chunk-EIBFAW37.js +1 -0
- package/dist/client/assets/chunks/chunk-EO3ER3GN.js +1 -0
- package/dist/client/assets/chunks/chunk-FH24BIIA.js +1 -0
- package/dist/client/assets/chunks/chunk-FNOXGAL5.js +2 -0
- package/dist/client/assets/chunks/chunk-FTY6WLQF.js +3 -0
- package/dist/client/assets/chunks/chunk-FZFME6UK.js +1 -0
- package/dist/client/assets/chunks/chunk-FZJZZYYO.js +1 -0
- package/dist/client/assets/chunks/chunk-G4Z6L7OO.js +1 -0
- package/dist/client/assets/chunks/chunk-GNCEAN2B.js +1 -0
- package/dist/client/assets/chunks/chunk-GQJIKV3T.js +1 -0
- package/dist/client/assets/chunks/chunk-GUMNOKT2.js +1 -0
- package/dist/client/assets/chunks/chunk-GXRORAF3.js +1 -0
- package/dist/client/assets/chunks/chunk-H36ABDKY.js +1 -0
- package/dist/client/assets/chunks/chunk-HAR5NYVW.js +1 -0
- package/dist/client/assets/chunks/chunk-HIGPS4L2.js +1 -0
- package/dist/client/assets/chunks/chunk-HMOBGTME.js +1 -0
- package/dist/client/assets/chunks/chunk-HNO6SQ7M.js +1 -0
- package/dist/client/assets/chunks/chunk-HYVZ3GYN.js +1 -0
- package/dist/client/assets/chunks/chunk-I6TGFOQS.js +1 -0
- package/dist/client/assets/chunks/chunk-IHIOJK7I.js +1 -0
- package/dist/client/assets/chunks/chunk-IHJNUMSC.js +1 -0
- package/dist/client/assets/chunks/chunk-IP7PB2OC.js +1 -0
- package/dist/client/assets/chunks/chunk-ISDTMH2Y.js +1 -0
- package/dist/client/assets/chunks/chunk-J65M2LNT.js +1 -0
- package/dist/client/assets/chunks/chunk-JCWYZ7X2.js +62 -0
- package/dist/client/assets/chunks/chunk-JENWYTLI.js +1 -0
- package/dist/client/assets/chunks/chunk-JJ6YWJQS.js +1 -0
- package/dist/client/assets/chunks/chunk-JMUVGGX7.js +32 -0
- package/dist/client/assets/chunks/chunk-JNQUFMM4.js +1 -0
- package/dist/client/assets/chunks/chunk-JPTECOAL.js +1 -0
- package/dist/client/assets/chunks/chunk-JZNK4LKS.js +1 -0
- package/dist/client/assets/chunks/chunk-KPNXJBIW.js +1 -0
- package/dist/client/assets/chunks/chunk-KZO2GZL7.js +1 -0
- package/dist/client/assets/chunks/chunk-L2REESOQ.js +1 -0
- package/dist/client/assets/chunks/chunk-LJPJXR7R.js +1 -0
- package/dist/client/assets/chunks/chunk-LOEZMLCI.js +1 -0
- package/dist/client/assets/chunks/chunk-MLYC6FSE.js +1 -0
- package/dist/client/assets/chunks/chunk-MMDGEZYU.js +1 -0
- package/dist/client/assets/chunks/chunk-MYXE7CGE.js +1 -0
- package/dist/client/assets/chunks/chunk-NBH7YOBD.js +15 -0
- package/dist/client/assets/chunks/chunk-NCOVM3YF.js +1 -0
- package/dist/client/assets/chunks/chunk-NCWOBVU7.js +1 -0
- package/dist/client/assets/chunks/chunk-NLRIHHEH.js +1 -0
- package/dist/client/assets/chunks/chunk-NUPEOYK6.js +1 -0
- package/dist/client/assets/chunks/chunk-O4MVOA7X.js +1 -0
- package/dist/client/assets/chunks/chunk-OBHQQZ4H.js +1 -0
- package/dist/client/assets/chunks/chunk-OKPNJIIR.js +1 -0
- package/dist/client/assets/chunks/chunk-OLMPB57R.js +1 -0
- package/dist/client/assets/chunks/chunk-P2FYOAEV.js +1 -0
- package/dist/client/assets/chunks/chunk-PI3SNP4C.js +1 -0
- package/dist/client/assets/chunks/chunk-PRDTEROB.js +1 -0
- package/dist/client/assets/chunks/chunk-PZELHD2H.js +136 -0
- package/dist/client/assets/chunks/chunk-QR2Q34LM.js +1 -0
- package/dist/client/assets/chunks/chunk-R4GIXL6S.js +1 -0
- package/dist/client/assets/chunks/chunk-RORF4F5V.js +1 -0
- package/dist/client/assets/chunks/chunk-S73BYTLL.js +1 -0
- package/dist/client/assets/chunks/chunk-SFC5MRYR.js +1 -0
- package/dist/client/assets/chunks/chunk-SVM2H22K.js +2 -0
- package/dist/client/assets/chunks/chunk-SZ6UCGVD.js +1 -0
- package/dist/client/assets/chunks/chunk-SZYM6GIJ.js +127 -0
- package/dist/client/assets/chunks/chunk-TT5KJZKM.js +1 -0
- package/dist/client/assets/chunks/chunk-UFBKUYNH.js +1 -0
- package/dist/client/assets/chunks/chunk-UL57AD4T.js +1 -0
- package/dist/client/assets/chunks/chunk-UNEI7LMM.js +1 -0
- package/dist/client/assets/chunks/chunk-VNQBBFIY.js +1 -0
- package/dist/client/assets/chunks/chunk-WGVITPII.js +1 -0
- package/dist/client/assets/chunks/chunk-WRA6KJHX.js +10 -0
- package/dist/client/assets/chunks/chunk-WS5EHW43.js +1 -0
- package/dist/client/assets/chunks/chunk-XE6SLBBE.js +3 -0
- package/dist/client/assets/chunks/chunk-XF5T2P7U.js +72 -0
- package/dist/client/assets/chunks/chunk-XFRY5WKO.js +1 -0
- package/dist/client/assets/chunks/chunk-XWYV4OPA.js +1 -0
- package/dist/client/assets/chunks/chunk-YCE5ECKY.js +1 -0
- package/dist/client/assets/chunks/chunk-YIN5CSIF.js +1 -0
- package/dist/client/assets/chunks/chunk-YLFUX6XH.js +1 -0
- package/dist/client/assets/chunks/chunk-YNXJICZM.js +1 -0
- package/dist/client/assets/chunks/chunk-YOHF7NGD.js +1 -0
- package/dist/client/assets/chunks/chunk-YORIST7Z.js +1 -0
- package/dist/client/assets/chunks/chunk-YUL7CG4R.js +1 -0
- package/dist/client/assets/chunks/chunk-YUN7JSFJ.js +2 -0
- package/dist/client/assets/chunks/chunk-YZ7D2IT7.js +1 -0
- package/dist/client/assets/chunks/chunk-Z7LAPMFJ.js +1 -0
- package/dist/client/assets/chunks/chunk-ZE325LPC.js +1 -0
- package/dist/client/assets/chunks/chunk-ZN7TN6PO.js +1 -0
- package/dist/client/assets/chunks/chunk-ZST3EVPO.js +1 -0
- package/dist/client/assets/chunks/chunk-ZZHMDDZN.js +1 -0
- package/dist/client/assets/chunks/clarity-RKRYQ7WF.js +1 -0
- package/dist/client/assets/chunks/classDiagram-v2-L25IHNXF-Y67HVDZV.js +217 -0
- package/dist/client/assets/chunks/clojure-2X2DQECJ.js +1 -0
- package/dist/client/assets/chunks/cmake-LH5524XE.js +1 -0
- package/dist/client/assets/chunks/cobol-7DALQF3W.js +1 -0
- package/dist/client/assets/chunks/codeowners-52SZU5P5.js +1 -0
- package/dist/client/assets/chunks/codeql-EISH3KUP.js +1 -0
- package/dist/client/assets/chunks/coffee-VAWSGA7Y.js +1 -0
- package/dist/client/assets/chunks/common-lisp-4MVK2AN6.js +1 -0
- package/dist/client/assets/chunks/coq-4VRYZRZV.js +1 -0
- package/dist/client/assets/chunks/core-BRBMZSD2.js +12 -0
- package/dist/client/assets/chunks/cose-bilkent-JH36ORCC-BIOOHSAA.js +1 -0
- package/dist/client/assets/chunks/cpp-RFECMSKD.js +1 -0
- package/dist/client/assets/chunks/crystal-ZBGGFAOI.js +1 -0
- package/dist/client/assets/chunks/csharp-BZDPMKKW.js +1 -0
- package/dist/client/assets/chunks/css-3WWGM6UD.js +1 -0
- package/dist/client/assets/chunks/csv-AB4GNWSB.js +1 -0
- package/dist/client/assets/chunks/cue-2E46MZP2.js +1 -0
- package/dist/client/assets/chunks/cynefin-EF2NZ3EQ-TLFQLWUN.js +1 -0
- package/dist/client/assets/chunks/cynefinDiagram-L36ESFBY-FLPXCSSG.js +62 -0
- package/dist/client/assets/chunks/cypher-HR3ILFOL.js +1 -0
- package/dist/client/assets/chunks/d-TARSZPOL.js +1 -0
- package/dist/client/assets/chunks/dagre-SPEB5RXH-7EGEINJ2.js +4 -0
- package/dist/client/assets/chunks/dart-SALLJHVI.js +1 -0
- package/dist/client/assets/chunks/dax-WYMRGMXC.js +1 -0
- package/dist/client/assets/chunks/desktop-ND5CGLLJ.js +1 -0
- package/dist/client/assets/chunks/diagram-3B7GL73J-6USY4HZX.js +200 -0
- package/dist/client/assets/chunks/diagram-DNEMPZML-BNSOC4VW.js +24 -0
- package/dist/client/assets/chunks/diagram-L3MUGCDN-5V5FZYIG.js +41 -0
- package/dist/client/assets/chunks/diagram-LA4NIPWU-25VLY2CO.js +24 -0
- package/dist/client/assets/chunks/diagram-MHCOC4E2-G2LFP3JU.js +3 -0
- package/dist/client/assets/chunks/diagram-XPFHEONP-E6HBBIBV.js +30 -0
- package/dist/client/assets/chunks/diff-PWYGPM7B.js +1 -0
- package/dist/client/assets/chunks/docker-A4D7DT7W.js +1 -0
- package/dist/client/assets/chunks/dotenv-VWQWV2DY.js +1 -0
- package/dist/client/assets/chunks/dream-maker-M5YDQS35.js +1 -0
- package/dist/client/assets/chunks/ebnfDiagram-UZSYDL2S-BMXXZZPI.js +1 -0
- package/dist/client/assets/chunks/edge-3QXIDJHI.js +1 -0
- package/dist/client/assets/chunks/elixir-3OX2UHIZ.js +1 -0
- package/dist/client/assets/chunks/elk-XV6XMXQE-IPR65FON.js +27 -0
- package/dist/client/assets/chunks/elm-CTS65NCT.js +1 -0
- package/dist/client/assets/chunks/emacs-lisp-HHS2IDO7.js +1 -0
- package/dist/client/assets/chunks/engine-javascript-DNHSF67B.js +141 -0
- package/dist/client/assets/chunks/erDiagram-EYBAPDEH-3GEHFWHG.js +99 -0
- package/dist/client/assets/chunks/erb-ICYWK6HS.js +1 -0
- package/dist/client/assets/chunks/erlang-BDQPNCKX.js +1 -0
- package/dist/client/assets/chunks/eventmodeling-K75KTNOO-B27BJTZL.js +1 -0
- package/dist/client/assets/chunks/fennel-4ONBA2PH.js +1 -0
- package/dist/client/assets/chunks/fish-ATLPBZSD.js +1 -0
- package/dist/client/assets/chunks/flowDiagram-MALGAFMO-5PCLR4AE.js +1 -0
- package/dist/client/assets/chunks/fluent-GGMONBVW.js +1 -0
- package/dist/client/assets/chunks/fortran-fixed-form-5MSBDRWQ.js +1 -0
- package/dist/client/assets/chunks/fortran-free-form-TT3RBL75.js +1 -0
- package/dist/client/assets/chunks/fsharp-PAGHNW4O.js +1 -0
- package/dist/client/assets/chunks/ganttDiagram-23REXITY-VE5MVBTO.js +292 -0
- package/dist/client/assets/chunks/gdresource-5OTP326D.js +1 -0
- package/dist/client/assets/chunks/gdscript-VPWHSCLF.js +1 -0
- package/dist/client/assets/chunks/gdshader-FYLPXFE5.js +1 -0
- package/dist/client/assets/chunks/genie-UYMEEMA2.js +1 -0
- package/dist/client/assets/chunks/gherkin-SU7D5DMY.js +1 -0
- package/dist/client/assets/chunks/git-commit-5CF5QAG7.js +1 -0
- package/dist/client/assets/chunks/git-rebase-X72BAUZI.js +1 -0
- package/dist/client/assets/chunks/gitGraph-VSP46ZUC-J6CJ5YZF.js +1 -0
- package/dist/client/assets/chunks/gitGraphDiagram-STJD7YCW-7YJGT6ZP.js +106 -0
- package/dist/client/assets/chunks/gleam-LR42YRKO.js +1 -0
- package/dist/client/assets/chunks/glimmer-js-JPF67FJU.js +1 -0
- package/dist/client/assets/chunks/glimmer-ts-BYPAVIZT.js +1 -0
- package/dist/client/assets/chunks/glsl-GTGUULBN.js +1 -0
- package/dist/client/assets/chunks/gn-NMDQA4K3.js +1 -0
- package/dist/client/assets/chunks/gnuplot-UUAMYBCZ.js +1 -0
- package/dist/client/assets/chunks/go-4RERSTNP.js +1 -0
- package/dist/client/assets/chunks/graphql-5A6RK6YF.js +1 -0
- package/dist/client/assets/chunks/groovy-O7BJONCU.js +1 -0
- package/dist/client/assets/chunks/hack-7QGTNPDP.js +1 -0
- package/dist/client/assets/chunks/haml-ON5GAWN4.js +1 -0
- package/dist/client/assets/chunks/handlebars-6UOQSUBP.js +1 -0
- package/dist/client/assets/chunks/haskell-OIZXZ26Y.js +1 -0
- package/dist/client/assets/chunks/haxe-7FFVS4FO.js +1 -0
- package/dist/client/assets/chunks/hcl-XFUFQJHV.js +1 -0
- package/dist/client/assets/chunks/hjson-QDBHMTD5.js +1 -0
- package/dist/client/assets/chunks/hlsl-4LP25FFN.js +1 -0
- package/dist/client/assets/chunks/html-7RNQXAAV.js +1 -0
- package/dist/client/assets/chunks/html-derivative-ESOY72Q2.js +1 -0
- package/dist/client/assets/chunks/http-SAD4PMBD.js +1 -0
- package/dist/client/assets/chunks/hurl-ZSJJXPQH.js +1 -0
- package/dist/client/assets/chunks/hxml-HJCC37RK.js +1 -0
- package/dist/client/assets/chunks/hy-CLDE3P4K.js +1 -0
- package/dist/client/assets/chunks/imba-REB34U5V.js +1 -0
- package/dist/client/assets/chunks/info-OHQRW6UA-EQQKXR77.js +1 -0
- package/dist/client/assets/chunks/infoDiagram-KBXO4MBG-F6KGZCGS.js +2 -0
- package/dist/client/assets/chunks/ini-PDFEMGRA.js +1 -0
- package/dist/client/assets/chunks/ishikawaDiagram-M35NCXHK-5D6ZOZ34.js +70 -0
- package/dist/client/assets/chunks/java-KB6XENAD.js +1 -0
- package/dist/client/assets/chunks/javascript-3JX5T423.js +1 -0
- package/dist/client/assets/chunks/jinja-V4UNU6K4.js +1 -0
- package/dist/client/assets/chunks/jison-KH5XNLJP.js +1 -0
- package/dist/client/assets/chunks/journeyDiagram-OPLOXDXO-ANDOJZKW.js +139 -0
- package/dist/client/assets/chunks/json-HPXFRRPY.js +1 -0
- package/dist/client/assets/chunks/json5-LJM6TXWM.js +1 -0
- package/dist/client/assets/chunks/jsonc-UZWRU3JV.js +1 -0
- package/dist/client/assets/chunks/jsonl-RFZD5UN4.js +1 -0
- package/dist/client/assets/chunks/jsonnet-GFZABBL6.js +1 -0
- package/dist/client/assets/chunks/jssm-2YIGBISJ.js +1 -0
- package/dist/client/assets/chunks/jsx-KLV2ZWUT.js +1 -0
- package/dist/client/assets/chunks/julia-3TZDIPPO.js +1 -0
- package/dist/client/assets/chunks/just-UHCONBQS.js +1 -0
- package/dist/client/assets/chunks/kanban-definition-BZKZHSQY-GF5I5NVD.js +89 -0
- package/dist/client/assets/chunks/katex-PIN72PQZ.js +257 -0
- package/dist/client/assets/chunks/kdl-DARTFOCL.js +1 -0
- package/dist/client/assets/chunks/kotlin-7KT5DW6Y.js +1 -0
- package/dist/client/assets/chunks/kusto-EKYXZTC5.js +1 -0
- package/dist/client/assets/chunks/langs-GO3UQR4H.js +1 -0
- package/dist/client/assets/chunks/latex-T57MIHR5.js +1 -0
- package/dist/client/assets/chunks/lean-5QVPRMV6.js +1 -0
- package/dist/client/assets/chunks/less-K6QEH5TN.js +1 -0
- package/dist/client/assets/chunks/liquid-3LCNNEIV.js +1 -0
- package/dist/client/assets/chunks/llvm-O32ZIVRF.js +1 -0
- package/dist/client/assets/chunks/log-C4YPJ5BB.js +1 -0
- package/dist/client/assets/chunks/logo-JPMO43A6.js +1 -0
- package/dist/client/assets/chunks/lua-YKVSOG7K.js +1 -0
- package/dist/client/assets/chunks/luau-K4EECYSO.js +1 -0
- package/dist/client/assets/chunks/make-VHSYMHHW.js +1 -0
- package/dist/client/assets/chunks/markdown-GC5J46H4.js +1 -0
- package/dist/client/assets/chunks/marko-U45YFADC.js +1 -0
- package/dist/client/assets/chunks/matlab-AMOKC4LU.js +1 -0
- package/dist/client/assets/chunks/mdc-DIGA2UXP.js +1 -0
- package/dist/client/assets/chunks/mdx-BZUGCKQA.js +1 -0
- package/dist/client/assets/chunks/mermaid-WHDP6ZPA.js +1 -0
- package/dist/client/assets/chunks/mermaid.core-SQMPMUDN.js +10 -0
- package/dist/client/assets/chunks/mindmap-definition-I2QAS2VK-4NH5S56C.js +98 -0
- package/dist/client/assets/chunks/mipsasm-2S4CUGQV.js +1 -0
- package/dist/client/assets/chunks/mojo-ZOL2PNKT.js +1 -0
- package/dist/client/assets/chunks/moonbit-U3BDAIFE.js +1 -0
- package/dist/client/assets/chunks/move-YSXALBWA.js +1 -0
- package/dist/client/assets/chunks/narrat-DHFIAUJH.js +1 -0
- package/dist/client/assets/chunks/nextflow-ESAKNTI3.js +1 -0
- package/dist/client/assets/chunks/nextflow-groovy-FKDSPJUI.js +1 -0
- package/dist/client/assets/chunks/nginx-XYAYERLO.js +1 -0
- package/dist/client/assets/chunks/nim-XOWBVFDH.js +1 -0
- package/dist/client/assets/chunks/nix-RVSTQSHC.js +1 -0
- package/dist/client/assets/chunks/nsis-KMDUKQRL.js +1 -0
- package/dist/client/assets/chunks/nushell-SKJHHZ7U.js +1 -0
- package/dist/client/assets/chunks/objective-c-3B3KXQ7Q.js +1 -0
- package/dist/client/assets/chunks/objective-cpp-6JDULPJS.js +1 -0
- package/dist/client/assets/chunks/ocaml-TMRFXBVO.js +1 -0
- package/dist/client/assets/chunks/odin-IN3PLCK7.js +1 -0
- package/dist/client/assets/chunks/openscad-P52IKPNH.js +1 -0
- package/dist/client/assets/chunks/org-N3SJ2PIA.js +1 -0
- package/dist/client/assets/chunks/packet-JDAUHWVQ-4SWIKSKP.js +1 -0
- package/dist/client/assets/chunks/pascal-T7CUWCQ4.js +1 -0
- package/dist/client/assets/chunks/pegDiagram-FD4XPPHP-WDKZSEGN.js +1 -0
- package/dist/client/assets/chunks/perl-LCZ4KODH.js +1 -0
- package/dist/client/assets/chunks/php-4XHMBVR5.js +1 -0
- package/dist/client/assets/chunks/pie-XZMESJXO-PSSWOWGV.js +1 -0
- package/dist/client/assets/chunks/pieDiagram-J4AKIIQI-BDW5I5ON.js +39 -0
- package/dist/client/assets/chunks/pkl-OACG5B67.js +1 -0
- package/dist/client/assets/chunks/plsql-7E5C7CJX.js +1 -0
- package/dist/client/assets/chunks/po-ZSREFRRP.js +1 -0
- package/dist/client/assets/chunks/polar-4GBINVA7.js +1 -0
- package/dist/client/assets/chunks/postcss-B7RPVGFW.js +1 -0
- package/dist/client/assets/chunks/powerquery-6UFMTY4G.js +1 -0
- package/dist/client/assets/chunks/powershell-3STRPCNT.js +1 -0
- package/dist/client/assets/chunks/prisma-4TPSPIZ5.js +1 -0
- package/dist/client/assets/chunks/prolog-HEAYPKQW.js +1 -0
- package/dist/client/assets/chunks/proto-Y3Y3VKXM.js +1 -0
- package/dist/client/assets/chunks/pug-7PBPMG3M.js +1 -0
- package/dist/client/assets/chunks/puppet-FR35R4OI.js +1 -0
- package/dist/client/assets/chunks/purescript-OUHXQAOW.js +1 -0
- package/dist/client/assets/chunks/python-SNDG7FNU.js +1 -0
- package/dist/client/assets/chunks/qml-UVIOSWP4.js +1 -0
- package/dist/client/assets/chunks/qmldir-6JGUNYL5.js +1 -0
- package/dist/client/assets/chunks/qss-ZCC466IZ.js +1 -0
- package/dist/client/assets/chunks/quadrantDiagram-XTYBLID2-5VKXWVJ6.js +7 -0
- package/dist/client/assets/chunks/r-4XW4O3ZF.js +1 -0
- package/dist/client/assets/chunks/racket-YJAYXOOG.js +1 -0
- package/dist/client/assets/chunks/radar-ABXABTNO-3AXZCEY2.js +1 -0
- package/dist/client/assets/chunks/railroad-I3PHUGI6-2PLXYPOT.js +1 -0
- package/dist/client/assets/chunks/railroad-abnf-LNEFI6M7-M4DGICQX.js +1 -0
- package/dist/client/assets/chunks/railroad-ebnf-DWYD2UWJ-UEIHQMSZ.js +1 -0
- package/dist/client/assets/chunks/railroad-peg-JR7BVNR7-I7VLIBXM.js +1 -0
- package/dist/client/assets/chunks/railroadDiagram-MRQ6NCDL-Y6IYWEFL.js +1 -0
- package/dist/client/assets/chunks/raku-63NEL5JK.js +1 -0
- package/dist/client/assets/chunks/razor-J3CJP523.js +1 -0
- package/dist/client/assets/chunks/rbs-6GQLT7NW.js +1 -0
- package/dist/client/assets/chunks/reg-6Q3OEC2C.js +1 -0
- package/dist/client/assets/chunks/regexp-LPIMILT3.js +1 -0
- package/dist/client/assets/chunks/rel-TOQGUTHB.js +1 -0
- package/dist/client/assets/chunks/requirementDiagram-Y7LWUY3S-CIWXVVWJ.js +84 -0
- package/dist/client/assets/chunks/riscv-UELGBOXE.js +1 -0
- package/dist/client/assets/chunks/ron-FW7LCJJH.js +1 -0
- package/dist/client/assets/chunks/rosmsg-O63VHE3L.js +1 -0
- package/dist/client/assets/chunks/rst-3WJXGLA7.js +1 -0
- package/dist/client/assets/chunks/ruby-7S7L6JTH.js +1 -0
- package/dist/client/assets/chunks/rust-LIK4HRRI.js +1 -0
- package/dist/client/assets/chunks/sankeyDiagram-WGJFYUXA-OGNQPX5V.js +40 -0
- package/dist/client/assets/chunks/sas-FHDPZ2JH.js +1 -0
- package/dist/client/assets/chunks/sass-UI4HW2FN.js +1 -0
- package/dist/client/assets/chunks/scala-KN7RQSUJ.js +1 -0
- package/dist/client/assets/chunks/scheme-5R527YZ5.js +1 -0
- package/dist/client/assets/chunks/scss-KZ6DRE4A.js +1 -0
- package/dist/client/assets/chunks/sdbl-FHOCL5SL.js +1 -0
- package/dist/client/assets/chunks/sequenceDiagram-5NDRBG5M-QX2V24ZU.js +169 -0
- package/dist/client/assets/chunks/shaderlab-2TLHUS2H.js +1 -0
- package/dist/client/assets/chunks/shellscript-PIB6ZBDM.js +1 -0
- package/dist/client/assets/chunks/shellsession-5KL7NFNN.js +1 -0
- package/dist/client/assets/chunks/sizeCapture-INFHLROL-LCS2PIHM.js +1 -0
- package/dist/client/assets/chunks/smalltalk-JFLFU247.js +1 -0
- package/dist/client/assets/chunks/smithy-HONEXZAM.js +1 -0
- package/dist/client/assets/chunks/solidity-SJ3INJFZ.js +1 -0
- package/dist/client/assets/chunks/soy-ND4SWTM2.js +1 -0
- package/dist/client/assets/chunks/sparql-YB2JQBZD.js +1 -0
- package/dist/client/assets/chunks/splunk-UXIDDAWL.js +1 -0
- package/dist/client/assets/chunks/sql-K6OMG7SQ.js +1 -0
- package/dist/client/assets/chunks/ssh-config-I7KJY3KQ.js +1 -0
- package/dist/client/assets/chunks/stata-DNZ3DTFJ.js +1 -0
- package/dist/client/assets/chunks/stateDiagram-v2-FJ4FJNEA-LVAUWF3A.js +291 -0
- package/dist/client/assets/chunks/stylus-GWPR6IQY.js +1 -0
- package/dist/client/assets/chunks/surrealql-KQNHBWLC.js +1 -0
- package/dist/client/assets/chunks/svelte-YQKF25S4.js +1 -0
- package/dist/client/assets/chunks/swift-ZCIZUJXA.js +1 -0
- package/dist/client/assets/chunks/swimlanes-UMQ2AFYF-B3RQOARQ.js +1 -0
- package/dist/client/assets/chunks/swimlanesDiagram-OMPLPG4O-JOE33EOV.js +8 -0
- package/dist/client/assets/chunks/system-verilog-Y7E6FIO3.js +1 -0
- package/dist/client/assets/chunks/systemd-WHC7NBZP.js +1 -0
- package/dist/client/assets/chunks/talonscript-WC7SODAH.js +1 -0
- package/dist/client/assets/chunks/tasl-QHY2GTCB.js +1 -0
- package/dist/client/assets/chunks/tcl-VAMIL5PJ.js +1 -0
- package/dist/client/assets/chunks/temml-ISNSX3VV.js +96 -0
- package/dist/client/assets/chunks/templ-B3BHKWV4.js +1 -0
- package/dist/client/assets/chunks/terraform-Q4UEK6FK.js +1 -0
- package/dist/client/assets/chunks/tex-63OWCYAB.js +1 -0
- package/dist/client/assets/chunks/timeline-definition-SEUHKTQE-FDFTVAJG.js +120 -0
- package/dist/client/assets/chunks/toml-7Z4IPGES.js +1 -0
- package/dist/client/assets/chunks/treeView-D4JQ5SDB-ABSGRAGI.js +1 -0
- package/dist/client/assets/chunks/treemap-SAJKECNS-MYX2CBJN.js +1 -0
- package/dist/client/assets/chunks/ts-tags-DT62HE52.js +1 -0
- package/dist/client/assets/chunks/tsv-RTXHWADP.js +1 -0
- package/dist/client/assets/chunks/tsx-UANUBOC7.js +1 -0
- package/dist/client/assets/chunks/turtle-QVOUNUUL.js +1 -0
- package/dist/client/assets/chunks/twig-ZPC4BPDA.js +1 -0
- package/dist/client/assets/chunks/typescript-WGWKIMMW.js +1 -0
- package/dist/client/assets/chunks/typespec-TYCVR32J.js +1 -0
- package/dist/client/assets/chunks/typst-PIBFYUO6.js +1 -0
- package/dist/client/assets/chunks/usecaseDiagram-AFA6YXN4-KW7ADINR.js +370 -0
- package/dist/client/assets/chunks/v-3ERNWBSP.js +1 -0
- package/dist/client/assets/chunks/vala-UCBCT2GS.js +1 -0
- package/dist/client/assets/chunks/vb-DGI3VTPA.js +1 -0
- package/dist/client/assets/chunks/vennDiagram-NNMJR3RB-RDGLGOGM.js +34 -0
- package/dist/client/assets/chunks/verilog-G72NZROS.js +1 -0
- package/dist/client/assets/chunks/vhdl-PYGA77XE.js +1 -0
- package/dist/client/assets/chunks/viml-OOC5B5UU.js +1 -0
- package/dist/client/assets/chunks/vue-GTNHIOPG.js +1 -0
- package/dist/client/assets/chunks/vue-html-DKAMJV55.js +1 -0
- package/dist/client/assets/chunks/vue-vine-WTM5HREV.js +1 -0
- package/dist/client/assets/chunks/vyper-RHYEHOJM.js +1 -0
- package/dist/client/assets/chunks/wardley-7MLQ67FV-QJL2HSBT.js +1 -0
- package/dist/client/assets/chunks/wardleyDiagram-S7YFUSEH-A4XX3UJR.js +78 -0
- package/dist/client/assets/chunks/wasm-ZMM2Z5XV.js +1 -0
- package/dist/client/assets/chunks/wenyan-HF5UTKSJ.js +1 -0
- package/dist/client/assets/chunks/wgsl-O7D7OPO7.js +1 -0
- package/dist/client/assets/chunks/wikitext-T7BSWSKQ.js +1 -0
- package/dist/client/assets/chunks/wit-TKCHPPT6.js +1 -0
- package/dist/client/assets/chunks/wolfram-7MN66A7N.js +1 -0
- package/dist/client/assets/chunks/xml-NLTLAWOD.js +1 -0
- package/dist/client/assets/chunks/xsl-NZ4ECK3P.js +1 -0
- package/dist/client/assets/chunks/xychartDiagram-GGNKY7IX-OXJILWHR.js +7 -0
- package/dist/client/assets/chunks/yaml-HRJLEV3V.js +1 -0
- package/dist/client/assets/chunks/zenscript-4LLRSLPI.js +1 -0
- package/dist/client/assets/chunks/zig-Y7MUQFUO.js +1 -0
- package/dist/client/assets/icon.svg +1 -0
- package/dist/client/assets/main.js +254 -0
- package/dist/client/index.html +59 -0
- package/dist/diff.js +119 -0
- package/dist/fs/files.js +233 -0
- package/dist/fs/git.js +369 -0
- package/dist/fs/glob.js +89 -0
- package/dist/fs/notes.js +368 -0
- package/dist/fs/search.js +136 -0
- package/dist/fs/watch.js +69 -0
- package/dist/fs/zip.js +100 -0
- package/dist/hooks.js +65 -0
- package/dist/http.js +81 -0
- package/dist/hub/atomic.js +77 -0
- package/dist/hub/notify.js +239 -0
- package/dist/hub/registry.js +219 -0
- package/dist/mcp.js +500 -0
- package/dist/q/agent.js +215 -0
- package/dist/q/model.js +633 -0
- package/dist/q/schema.js +133 -0
- package/dist/q/store.js +413 -0
- package/dist/r/model.js +323 -0
- package/dist/r/schema.js +53 -0
- package/dist/r/store.js +489 -0
- package/dist/server.js +246 -0
- package/dist/settings.js +30 -0
- package/dist/workspace.js +639 -0
- package/package.json +73 -4
- package/schema/questions.schema.json +1670 -0
- package/schema/reviews.schema.json +151 -0
package/dist/q/model.js
ADDED
|
@@ -0,0 +1,633 @@
|
|
|
1
|
+
// The question-set format: what an LLM writes (lenient, with shorthands), the normalised form everything else reads,
|
|
2
|
+
// validation, which questions are visible given the answers so far, fingerprints, summaries and answer text.
|
|
3
|
+
// Pure: no Node APIs, so the page bundles it too.
|
|
4
|
+
export const TYPES = ['single', 'multi', 'yesno', 'text', 'number', 'scale', 'rank'];
|
|
5
|
+
export const PRIORITIES = ['blocking', 'high', 'normal', 'low'];
|
|
6
|
+
// ── answers ──
|
|
7
|
+
export const STATUSES = ['answered', 'delegated', 'skipped', 'later'];
|
|
8
|
+
export const emptyAnswers = () => ({ version: 1, seq: 0, readSeq: 0, answers: {}, threads: {} });
|
|
9
|
+
// ── helpers ──
|
|
10
|
+
export const SET_ID = /^[a-z0-9][a-z0-9._-]{0,79}$/i;
|
|
11
|
+
const ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,79}$/;
|
|
12
|
+
export function slug(s, max = 40) {
|
|
13
|
+
return (s
|
|
14
|
+
.toLowerCase()
|
|
15
|
+
.normalize('NFKD')
|
|
16
|
+
.replace(/[̀-ͯ]/g, '')
|
|
17
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
18
|
+
.replace(/^-+|-+$/g, '')
|
|
19
|
+
.slice(0, max)
|
|
20
|
+
.replace(/-+$/, '') || 'x');
|
|
21
|
+
}
|
|
22
|
+
/** A short, stable fingerprint (FNV-1a, 32-bit, base 36). */
|
|
23
|
+
export function hash(s) {
|
|
24
|
+
let h = 0x811c9dc5;
|
|
25
|
+
for (let i = 0; i < s.length; i++) {
|
|
26
|
+
h ^= s.charCodeAt(i);
|
|
27
|
+
h = Math.imul(h, 0x01000193);
|
|
28
|
+
}
|
|
29
|
+
return (h >>> 0).toString(36);
|
|
30
|
+
}
|
|
31
|
+
const arr = (v) => (v === undefined ? [] : Array.isArray(v) ? v.map(String) : [String(v)]);
|
|
32
|
+
const str = (v) => (typeof v === 'string' ? v : v === undefined || v === null ? '' : String(v));
|
|
33
|
+
const num = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : undefined);
|
|
34
|
+
const isObj = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
|
|
35
|
+
/** an object, without narrowing what it is */
|
|
36
|
+
const plain = (v) => isObj(v);
|
|
37
|
+
function blocks(input, where, errors) {
|
|
38
|
+
if (input === undefined || input === null)
|
|
39
|
+
return [];
|
|
40
|
+
const list = Array.isArray(input) ? input : [input];
|
|
41
|
+
const out = [];
|
|
42
|
+
list.forEach((b, i) => {
|
|
43
|
+
const at = `${where}.content[${i}]`;
|
|
44
|
+
if (typeof b === 'string')
|
|
45
|
+
return void out.push({ kind: 'md', md: b });
|
|
46
|
+
if (!isObj(b))
|
|
47
|
+
return void errors.push(`${at}: a block is a markdown string or an object`);
|
|
48
|
+
const common = {};
|
|
49
|
+
if (typeof b['title'] === 'string')
|
|
50
|
+
common.title = b['title'];
|
|
51
|
+
if (b['collapsed'] === true)
|
|
52
|
+
common.collapsed = true;
|
|
53
|
+
if (typeof b['md'] === 'string')
|
|
54
|
+
out.push({ kind: 'md', md: b['md'], ...common });
|
|
55
|
+
else if (typeof b['file'] === 'string') {
|
|
56
|
+
const lines = b['lines'] === undefined ? undefined : str(b['lines']);
|
|
57
|
+
if (lines !== undefined && !/^\d+(-\d+)?$/.test(lines))
|
|
58
|
+
errors.push(`${at}: lines is "12" or "10-40"`);
|
|
59
|
+
if (/^([a-z]:|\/|\\)/i.test(b['file']) || b['file'].split(/[\\/]/).includes('..'))
|
|
60
|
+
errors.push(`${at}: file is a path inside the project folder (no absolute paths or ..)`);
|
|
61
|
+
out.push({ kind: 'file', file: b['file'].replace(/\\/g, '/'), ...(lines ? { lines } : {}), ...common });
|
|
62
|
+
}
|
|
63
|
+
else if (typeof b['code'] === 'string')
|
|
64
|
+
out.push({ kind: 'code', code: b['code'], lang: str(b['lang']), ...common });
|
|
65
|
+
else if (typeof b['diff'] === 'string')
|
|
66
|
+
out.push({ kind: 'diff', diff: b['diff'], ...common });
|
|
67
|
+
else if (typeof b['mermaid'] === 'string')
|
|
68
|
+
out.push({ kind: 'mermaid', mermaid: b['mermaid'], ...common });
|
|
69
|
+
else if (typeof b['image'] === 'string')
|
|
70
|
+
out.push({ kind: 'image', image: b['image'], alt: str(b['alt']), ...common });
|
|
71
|
+
else if (typeof b['html'] === 'string')
|
|
72
|
+
out.push({ kind: 'html', html: b['html'], height: Math.min(2000, Math.max(80, num(b['height']) ?? 320)), ...common });
|
|
73
|
+
else if (typeof b['link'] === 'string') {
|
|
74
|
+
if (!/^https?:\/\//i.test(b['link']))
|
|
75
|
+
errors.push(`${at}: link must be an http(s) URL`);
|
|
76
|
+
out.push({ kind: 'link', link: b['link'], label: str(b['label']) || b['link'], ...common });
|
|
77
|
+
}
|
|
78
|
+
else
|
|
79
|
+
errors.push(`${at}: unknown block (use md, file, code, diff, mermaid, image, html or link)`);
|
|
80
|
+
});
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
function conditions(input, where, errors) {
|
|
84
|
+
if (input === undefined || input === null)
|
|
85
|
+
return undefined;
|
|
86
|
+
const list = Array.isArray(input) ? input : [input];
|
|
87
|
+
const out = [];
|
|
88
|
+
for (const c of list) {
|
|
89
|
+
if (!isObj(c) || typeof c['question'] !== 'string') {
|
|
90
|
+
errors.push(`${where}.showIf: each condition needs a "question" id`);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const one = { question: c['question'] };
|
|
94
|
+
for (const k of ['any', 'all', 'none'])
|
|
95
|
+
if (c[k] !== undefined)
|
|
96
|
+
one[k] = arr(c[k]);
|
|
97
|
+
if (typeof c['answered'] === 'boolean')
|
|
98
|
+
one.answered = c['answered'];
|
|
99
|
+
if (num(c['gte']) !== undefined)
|
|
100
|
+
one.gte = num(c['gte']);
|
|
101
|
+
if (num(c['lte']) !== undefined)
|
|
102
|
+
one.lte = num(c['lte']);
|
|
103
|
+
out.push(one);
|
|
104
|
+
}
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
107
|
+
/** Normalise a set as written; errors make it unusable in places, warnings don't. */
|
|
108
|
+
export function normalise(id, input) {
|
|
109
|
+
const errors = [];
|
|
110
|
+
const warnings = [];
|
|
111
|
+
const raw = isObj(input) ? input : {};
|
|
112
|
+
if (!isObj(input))
|
|
113
|
+
errors.push('A set is a JSON object');
|
|
114
|
+
const set = {
|
|
115
|
+
id,
|
|
116
|
+
title: str(raw.title) || id,
|
|
117
|
+
intro: str(raw.intro),
|
|
118
|
+
content: blocks(raw.content, 'set', errors),
|
|
119
|
+
status: raw.status === 'closed' ? 'closed' : 'open',
|
|
120
|
+
from: str(raw.from),
|
|
121
|
+
created: str(raw.created),
|
|
122
|
+
updated: str(raw.updated),
|
|
123
|
+
sections: [],
|
|
124
|
+
};
|
|
125
|
+
const ids = new Map(); // id → what it is
|
|
126
|
+
const claim = (id, what, where) => {
|
|
127
|
+
if (ids.has(id))
|
|
128
|
+
errors.push(`${where}: the id "${id}" is used twice (also ${ids.get(id)})`);
|
|
129
|
+
ids.set(id, what);
|
|
130
|
+
};
|
|
131
|
+
const rawSections = Array.isArray(raw.sections) ? raw.sections : [];
|
|
132
|
+
if (raw.sections !== undefined && !Array.isArray(raw.sections))
|
|
133
|
+
errors.push('sections must be a list');
|
|
134
|
+
const all = [];
|
|
135
|
+
if (Array.isArray(raw.questions) && raw.questions.length)
|
|
136
|
+
all.push({ id: '_', title: '', questions: raw.questions });
|
|
137
|
+
all.push(...rawSections);
|
|
138
|
+
let index = 0;
|
|
139
|
+
const autoIds = new Set();
|
|
140
|
+
all.forEach((s, si) => {
|
|
141
|
+
const where = s.id === '_' ? 'questions' : `sections[${si - (all[0]?.id === '_' ? 1 : 0)}]`;
|
|
142
|
+
if (!plain(s))
|
|
143
|
+
return void errors.push(`${where}: a section is an object`);
|
|
144
|
+
const sid = str(s.id) || (s.title ? slug(s.title) : `section-${si + 1}`);
|
|
145
|
+
if (sid !== '_' && !ID.test(sid))
|
|
146
|
+
errors.push(`${where}: bad id "${sid}" (letters, digits, . _ : -)`);
|
|
147
|
+
if (sid !== '_')
|
|
148
|
+
claim(sid, 'a section', where);
|
|
149
|
+
const section = {
|
|
150
|
+
id: sid,
|
|
151
|
+
title: str(s.title),
|
|
152
|
+
intro: str(s.intro),
|
|
153
|
+
content: blocks(s.content, where, errors),
|
|
154
|
+
hidden: s.hidden === true,
|
|
155
|
+
showIf: conditions(s.showIf, where, errors),
|
|
156
|
+
questions: [],
|
|
157
|
+
};
|
|
158
|
+
const qs = Array.isArray(s.questions) ? s.questions : [];
|
|
159
|
+
if (s.questions !== undefined && !Array.isArray(s.questions))
|
|
160
|
+
errors.push(`${where}.questions must be a list`);
|
|
161
|
+
qs.forEach((qi, qn) => {
|
|
162
|
+
const qwhere = `${where === 'questions' ? 'questions' : where + '.questions'}[${qn}]`;
|
|
163
|
+
if (!plain(qi))
|
|
164
|
+
return void errors.push(`${qwhere}: a question is an object`);
|
|
165
|
+
const text = str(qi.question ?? qi.q);
|
|
166
|
+
if (!text.trim())
|
|
167
|
+
errors.push(`${qwhere}: "question" is missing`);
|
|
168
|
+
let qid = str(qi.id);
|
|
169
|
+
if (!qid) {
|
|
170
|
+
qid = slug(text || `q${index + 1}`);
|
|
171
|
+
let n = 2;
|
|
172
|
+
while (ids.has(qid) || autoIds.has(qid))
|
|
173
|
+
qid = `${slug(text || 'q')}-${n++}`;
|
|
174
|
+
autoIds.add(qid);
|
|
175
|
+
warnings.push(`${qwhere}: no id, so it is "${qid}" (give each question an id so answers stay attached)`);
|
|
176
|
+
}
|
|
177
|
+
if (!ID.test(qid))
|
|
178
|
+
errors.push(`${qwhere}: bad id "${qid}" (letters, digits, . _ : -)`);
|
|
179
|
+
claim(qid, 'a question', qwhere);
|
|
180
|
+
const opts = Array.isArray(qi.options) ? qi.options : [];
|
|
181
|
+
const type = qi.type && TYPES.includes(qi.type) ? qi.type : opts.length ? 'single' : 'text';
|
|
182
|
+
if (qi.type && !TYPES.includes(qi.type))
|
|
183
|
+
errors.push(`${qwhere}: unknown type "${qi.type}" (${TYPES.join(', ')})`);
|
|
184
|
+
const options = [];
|
|
185
|
+
const optIds = new Set();
|
|
186
|
+
const source = type === 'yesno' && !opts.length ? [{ id: 'yes', label: 'Yes' }, { id: 'no', label: 'No' }] : opts;
|
|
187
|
+
source.forEach((o, on) => {
|
|
188
|
+
const owhere = `${qwhere}.options[${on}]`;
|
|
189
|
+
const oi = typeof o === 'string' ? { label: o } : plain(o) ? o : {};
|
|
190
|
+
if (!plain(o) && typeof o !== 'string')
|
|
191
|
+
errors.push(`${owhere}: an option is a string or an object`);
|
|
192
|
+
const label = str(oi.label ?? oi.t);
|
|
193
|
+
if (!label.trim())
|
|
194
|
+
errors.push(`${owhere}: "label" is missing`);
|
|
195
|
+
let oid = str(oi.id) || slug(label, 32);
|
|
196
|
+
let n = 2;
|
|
197
|
+
if (!oi.id)
|
|
198
|
+
while (optIds.has(oid))
|
|
199
|
+
oid = `${slug(label, 32)}-${n++}`;
|
|
200
|
+
if (optIds.has(oid))
|
|
201
|
+
errors.push(`${owhere}: the option id "${oid}" is used twice`);
|
|
202
|
+
optIds.add(oid);
|
|
203
|
+
options.push({
|
|
204
|
+
id: oid,
|
|
205
|
+
label,
|
|
206
|
+
description: str(oi.description ?? oi.d),
|
|
207
|
+
recommended: oi.recommended === true || oi.r === true,
|
|
208
|
+
content: blocks(oi.content, owhere, errors),
|
|
209
|
+
shows: arr(oi.shows),
|
|
210
|
+
hides: arr(oi.hides),
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
if ((type === 'single' || type === 'multi' || type === 'rank') && !options.length) {
|
|
214
|
+
errors.push(`${qwhere}: a ${type} question needs options`);
|
|
215
|
+
}
|
|
216
|
+
// the recommendation: on the question, or flagged on options
|
|
217
|
+
let recommended;
|
|
218
|
+
if (qi.recommended !== undefined) {
|
|
219
|
+
if (typeof qi.recommended === 'number')
|
|
220
|
+
recommended = qi.recommended;
|
|
221
|
+
else if (type === 'text')
|
|
222
|
+
recommended = arr(qi.recommended).join(', ');
|
|
223
|
+
else if (type === 'number' || type === 'scale') {
|
|
224
|
+
const n = Number(qi.recommended);
|
|
225
|
+
if (Number.isFinite(n))
|
|
226
|
+
recommended = n;
|
|
227
|
+
else
|
|
228
|
+
errors.push(`${qwhere}: recommended must be a number`);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
const r = arr(qi.recommended).map((x) => (optIds.has(x) ? x : (options.find((o) => o.label === x)?.id ?? x)));
|
|
232
|
+
for (const x of r)
|
|
233
|
+
if (!optIds.has(x))
|
|
234
|
+
errors.push(`${qwhere}: recommended "${x}" isn't one of the options`);
|
|
235
|
+
recommended = r;
|
|
236
|
+
for (const o of options)
|
|
237
|
+
if (r.includes(o.id))
|
|
238
|
+
o.recommended = true;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
else if (options.some((o) => o.recommended))
|
|
242
|
+
recommended = options.filter((o) => o.recommended).map((o) => o.id);
|
|
243
|
+
if (type === 'single' && Array.isArray(recommended) && recommended.length > 1)
|
|
244
|
+
warnings.push(`${qwhere}: a single-choice question recommends ${recommended.length} options`);
|
|
245
|
+
const priority = qi.blocking === true ? 'blocking' : qi.priority && PRIORITIES.includes(qi.priority) ? qi.priority : 'normal';
|
|
246
|
+
if (qi.priority && !PRIORITIES.includes(qi.priority))
|
|
247
|
+
errors.push(`${qwhere}: unknown priority "${qi.priority}" (${PRIORITIES.join(', ')})`);
|
|
248
|
+
const min = num(qi.min) ?? (type === 'scale' ? 1 : undefined);
|
|
249
|
+
const max = num(qi.max) ?? (type === 'scale' ? 5 : undefined);
|
|
250
|
+
if (min !== undefined && max !== undefined && min > max)
|
|
251
|
+
errors.push(`${qwhere}: min is above max`);
|
|
252
|
+
const q = {
|
|
253
|
+
id: qid,
|
|
254
|
+
section: sid,
|
|
255
|
+
question: text,
|
|
256
|
+
type,
|
|
257
|
+
header: str(qi.header),
|
|
258
|
+
why: str(qi.why),
|
|
259
|
+
content: blocks(qi.content, qwhere, errors),
|
|
260
|
+
options,
|
|
261
|
+
recommended,
|
|
262
|
+
priority,
|
|
263
|
+
hidden: qi.hidden === true,
|
|
264
|
+
showIf: conditions(qi.showIf, qwhere, errors),
|
|
265
|
+
allowOther: (type === 'single' || type === 'multi') && qi.allowOther !== false,
|
|
266
|
+
allowNote: qi.allowNote !== false,
|
|
267
|
+
min,
|
|
268
|
+
max,
|
|
269
|
+
step: num(qi.step),
|
|
270
|
+
unit: str(qi.unit),
|
|
271
|
+
minLabel: str(qi.minLabel),
|
|
272
|
+
maxLabel: str(qi.maxLabel),
|
|
273
|
+
placeholder: str(qi.placeholder),
|
|
274
|
+
multiline: qi.multiline !== false,
|
|
275
|
+
tags: Array.isArray(qi.tags) ? qi.tags.map(String) : [],
|
|
276
|
+
rev: '',
|
|
277
|
+
index: index++,
|
|
278
|
+
};
|
|
279
|
+
q.rev = fingerprint(q);
|
|
280
|
+
section.questions.push(q);
|
|
281
|
+
});
|
|
282
|
+
set.sections.push(section);
|
|
283
|
+
});
|
|
284
|
+
// every reference must point somewhere
|
|
285
|
+
const known = (ref) => ids.has(ref);
|
|
286
|
+
const checkCond = (cs, where) => {
|
|
287
|
+
for (const c of cs ?? []) {
|
|
288
|
+
if (!known(c.question) || ids.get(c.question) !== 'a question') {
|
|
289
|
+
errors.push(`${where}: showIf names "${c.question}", which isn't a question in this set`);
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
const target = questionById(set, c.question);
|
|
293
|
+
for (const k of ['any', 'all', 'none'])
|
|
294
|
+
for (const o of c[k] ?? [])
|
|
295
|
+
if (target && target.options.length && !target.options.some((x) => x.id === o))
|
|
296
|
+
errors.push(`${where}: showIf ${k} "${o}" isn't an option of "${c.question}"`);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
for (const s of set.sections) {
|
|
300
|
+
checkCond(s.showIf, `section "${s.id}"`);
|
|
301
|
+
for (const q of s.questions) {
|
|
302
|
+
checkCond(q.showIf, `question "${q.id}"`);
|
|
303
|
+
for (const o of q.options)
|
|
304
|
+
for (const ref of [...o.shows, ...o.hides]) {
|
|
305
|
+
if (!known(ref))
|
|
306
|
+
errors.push(`question "${q.id}" option "${o.id}": shows/hides "${ref}", which isn't a question or section here`);
|
|
307
|
+
else if (ref === q.id || ref === q.section)
|
|
308
|
+
warnings.push(`question "${q.id}" option "${o.id}": shows/hides itself`);
|
|
309
|
+
}
|
|
310
|
+
if (q.hidden && !q.showIf && !revealers(set).has(q.id) && !revealers(set).has(q.section))
|
|
311
|
+
warnings.push(`question "${q.id}" is hidden and nothing shows it`);
|
|
312
|
+
}
|
|
313
|
+
if (s.hidden && !s.showIf && !revealers(set).has(s.id))
|
|
314
|
+
warnings.push(`section "${s.id}" is hidden and nothing shows it`);
|
|
315
|
+
}
|
|
316
|
+
return { set, errors, warnings };
|
|
317
|
+
}
|
|
318
|
+
const revealCache = new WeakMap();
|
|
319
|
+
function revealers(set) {
|
|
320
|
+
let r = revealCache.get(set);
|
|
321
|
+
if (!r) {
|
|
322
|
+
r = new Set();
|
|
323
|
+
for (const q of allQuestions(set))
|
|
324
|
+
for (const o of q.options)
|
|
325
|
+
for (const id of o.shows)
|
|
326
|
+
r.add(id);
|
|
327
|
+
revealCache.set(set, r);
|
|
328
|
+
}
|
|
329
|
+
return r;
|
|
330
|
+
}
|
|
331
|
+
export function fingerprint(q) {
|
|
332
|
+
return hash(JSON.stringify([q.question.trim(), q.type, q.options.map((o) => [o.id, o.label.trim()]), q.min ?? null, q.max ?? null]));
|
|
333
|
+
}
|
|
334
|
+
export function allQuestions(set) {
|
|
335
|
+
return set.sections.flatMap((s) => s.questions);
|
|
336
|
+
}
|
|
337
|
+
export function questionById(set, id) {
|
|
338
|
+
for (const s of set.sections)
|
|
339
|
+
for (const q of s.questions)
|
|
340
|
+
if (q.id === id)
|
|
341
|
+
return q;
|
|
342
|
+
return undefined;
|
|
343
|
+
}
|
|
344
|
+
// ── answers: what they say ──
|
|
345
|
+
/** Options in effect: picked ones, or the recommendation when the owner said "you decide". */
|
|
346
|
+
export function effectivePicks(q, a) {
|
|
347
|
+
if (!a)
|
|
348
|
+
return [];
|
|
349
|
+
if (a.status === 'answered')
|
|
350
|
+
return a.picked ?? [];
|
|
351
|
+
if (a.status === 'delegated' && Array.isArray(q.recommended))
|
|
352
|
+
return q.recommended;
|
|
353
|
+
return [];
|
|
354
|
+
}
|
|
355
|
+
/** An answer counts as done when answered, delegated or skipped ("later" is still to do). */
|
|
356
|
+
export const isDone = (a) => !!a && a.status !== 'later';
|
|
357
|
+
/** The answer was given to an earlier version of the question. */
|
|
358
|
+
export const isStale = (q, a) => !!a && a.status !== 'skipped' && a.rev !== q.rev;
|
|
359
|
+
/**
|
|
360
|
+
* Which questions and sections show, given the answers. Each starts from its default (shown, unless `hidden`), or from
|
|
361
|
+
* its `showIf` when it has one. A picked option in a visible question then shows (`shows`) or hides (`hides`) the
|
|
362
|
+
* questions and sections it names; hiding wins. A question in a hidden section is hidden. Picks in hidden questions
|
|
363
|
+
* don't count, so whole branches fold away.
|
|
364
|
+
*/
|
|
365
|
+
export function visibility(set, answers) {
|
|
366
|
+
const qs = new Map(allQuestions(set).map((q) => [q.id, q]));
|
|
367
|
+
const sections = new Map(set.sections.map((s) => [s.id, s]));
|
|
368
|
+
const showers = new Map();
|
|
369
|
+
const hiders = new Map();
|
|
370
|
+
for (const q of qs.values())
|
|
371
|
+
for (const o of q.options) {
|
|
372
|
+
for (const t of o.shows)
|
|
373
|
+
(showers.get(t) ?? showers.set(t, []).get(t))?.push({ q: q.id, o: o.id });
|
|
374
|
+
for (const t of o.hides)
|
|
375
|
+
(hiders.get(t) ?? hiders.set(t, []).get(t))?.push({ q: q.id, o: o.id });
|
|
376
|
+
}
|
|
377
|
+
const memo = new Map();
|
|
378
|
+
const visiting = new Set();
|
|
379
|
+
const picked = (qid, oid) => {
|
|
380
|
+
const q = qs.get(qid);
|
|
381
|
+
return !!q && visibleQ(qid) && effectivePicks(q, answers[qid]).includes(oid);
|
|
382
|
+
};
|
|
383
|
+
const cond = (c) => {
|
|
384
|
+
const q = qs.get(c.question);
|
|
385
|
+
if (!q || !visibleQ(c.question))
|
|
386
|
+
return false;
|
|
387
|
+
const a = answers[c.question];
|
|
388
|
+
const done = !!a && a.status !== 'later' && a.status !== 'skipped';
|
|
389
|
+
if (c.answered !== undefined && c.answered !== done)
|
|
390
|
+
return false;
|
|
391
|
+
const picks = effectivePicks(q, a);
|
|
392
|
+
if (c.any && !c.any.some((o) => picks.includes(o)))
|
|
393
|
+
return false;
|
|
394
|
+
if (c.all && !c.all.every((o) => picks.includes(o)))
|
|
395
|
+
return false;
|
|
396
|
+
if (c.none && (!done || c.none.some((o) => picks.includes(o))))
|
|
397
|
+
return false;
|
|
398
|
+
if (c.gte !== undefined || c.lte !== undefined) {
|
|
399
|
+
const n = a?.status === 'answered' ? (a.number ?? NaN) : a?.status === 'delegated' && typeof q.recommended === 'number' ? q.recommended : NaN;
|
|
400
|
+
if (!Number.isFinite(n))
|
|
401
|
+
return false;
|
|
402
|
+
if (c.gte !== undefined && n < c.gte)
|
|
403
|
+
return false;
|
|
404
|
+
if (c.lte !== undefined && n > c.lte)
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
return true;
|
|
408
|
+
};
|
|
409
|
+
const decide = (key, base) => {
|
|
410
|
+
if (memo.has(key))
|
|
411
|
+
return memo.get(key) ?? false;
|
|
412
|
+
if (visiting.has(key))
|
|
413
|
+
return false; // a loop: whatever closes it doesn't count
|
|
414
|
+
visiting.add(key);
|
|
415
|
+
const id = key.slice(2);
|
|
416
|
+
let v;
|
|
417
|
+
if ((hiders.get(id) ?? []).some((h) => picked(h.q, h.o)))
|
|
418
|
+
v = false;
|
|
419
|
+
else if ((showers.get(id) ?? []).some((h) => picked(h.q, h.o)))
|
|
420
|
+
v = true;
|
|
421
|
+
else
|
|
422
|
+
v = base();
|
|
423
|
+
visiting.delete(key);
|
|
424
|
+
memo.set(key, v);
|
|
425
|
+
return v;
|
|
426
|
+
};
|
|
427
|
+
function visibleS(id) {
|
|
428
|
+
const s = sections.get(id);
|
|
429
|
+
if (!s)
|
|
430
|
+
return false;
|
|
431
|
+
return decide('s:' + id, () => (s.showIf ? s.showIf.every(cond) : !s.hidden));
|
|
432
|
+
}
|
|
433
|
+
function visibleQ(id) {
|
|
434
|
+
const q = qs.get(id);
|
|
435
|
+
if (!q)
|
|
436
|
+
return false;
|
|
437
|
+
if (!visibleS(q.section))
|
|
438
|
+
return false;
|
|
439
|
+
return decide('q:' + id, () => (q.showIf ? q.showIf.every(cond) : !q.hidden));
|
|
440
|
+
}
|
|
441
|
+
return { question: visibleQ, section: visibleS };
|
|
442
|
+
}
|
|
443
|
+
export function summarise(set, af, errors = 0) {
|
|
444
|
+
const vis = visibility(set, af.answers);
|
|
445
|
+
let visible = 0, done = 0, blocking = 0, later = 0, changed = 0, unread = 0, unacked = 0;
|
|
446
|
+
const qs = allQuestions(set);
|
|
447
|
+
for (const q of qs) {
|
|
448
|
+
const a = af.answers[q.id];
|
|
449
|
+
if (a && a.seq > af.readSeq)
|
|
450
|
+
unread++;
|
|
451
|
+
if (!vis.question(q.id))
|
|
452
|
+
continue;
|
|
453
|
+
visible++;
|
|
454
|
+
if (isDone(a))
|
|
455
|
+
done++;
|
|
456
|
+
else if (q.priority === 'blocking')
|
|
457
|
+
blocking++;
|
|
458
|
+
if (a?.status === 'later')
|
|
459
|
+
later++;
|
|
460
|
+
if (isStale(q, a))
|
|
461
|
+
changed++;
|
|
462
|
+
if (isDone(a) && a?.status !== 'skipped' && !a?.ack)
|
|
463
|
+
unacked++;
|
|
464
|
+
}
|
|
465
|
+
let unreadMessages = 0;
|
|
466
|
+
for (const list of Object.values(af.threads))
|
|
467
|
+
for (const m of list)
|
|
468
|
+
if (m.from === 'owner' && m.seq > af.readSeq)
|
|
469
|
+
unreadMessages++;
|
|
470
|
+
let updated = set.updated;
|
|
471
|
+
for (const a of Object.values(af.answers))
|
|
472
|
+
if (a.at > updated)
|
|
473
|
+
updated = a.at;
|
|
474
|
+
return { id: set.id, kind: 'questions', title: set.title, from: set.from, status: set.status, total: qs.length, visible, done, toAnswer: visible - done, blocking, later, changed, unread, unreadMessages, unacked, updated, errors };
|
|
475
|
+
}
|
|
476
|
+
// ── answers as text ──
|
|
477
|
+
export function answerText(q, a) {
|
|
478
|
+
const parts = [];
|
|
479
|
+
switch (a.status) {
|
|
480
|
+
case 'skipped':
|
|
481
|
+
parts.push('(skipped)');
|
|
482
|
+
break;
|
|
483
|
+
case 'later':
|
|
484
|
+
parts.push('(later)');
|
|
485
|
+
break;
|
|
486
|
+
case 'delegated': {
|
|
487
|
+
const rec = q ? recommendationText(q) : '';
|
|
488
|
+
parts.push(rec ? `(you decide; recommended: ${rec})` : '(you decide)');
|
|
489
|
+
break;
|
|
490
|
+
}
|
|
491
|
+
default: {
|
|
492
|
+
if (a.picked?.length) {
|
|
493
|
+
const labels = a.picked.map((id, i) => q?.options.find((o) => o.id === id)?.label ?? a.labels?.[i] ?? id);
|
|
494
|
+
parts.push(labels.join('; '));
|
|
495
|
+
}
|
|
496
|
+
if (a.other)
|
|
497
|
+
parts.push(`Other: ${a.other}`);
|
|
498
|
+
if (a.text !== undefined && a.text !== '')
|
|
499
|
+
parts.push(a.text);
|
|
500
|
+
if (a.number !== undefined)
|
|
501
|
+
parts.push(`${a.number}${q?.type === 'scale' && q.max !== undefined ? ` (of ${q.min ?? 1}–${q.max})` : q?.unit ? ' ' + q.unit : ''}`);
|
|
502
|
+
if (a.order?.length)
|
|
503
|
+
parts.push(a.order.map((id, i) => `${i + 1}. ${q?.options.find((o) => o.id === id)?.label ?? id}`).join(' '));
|
|
504
|
+
if (!parts.length)
|
|
505
|
+
parts.push('(no answer)');
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return parts.join(' · ');
|
|
509
|
+
}
|
|
510
|
+
export function recommendationText(q) {
|
|
511
|
+
if (q.recommended === undefined)
|
|
512
|
+
return '';
|
|
513
|
+
if (Array.isArray(q.recommended))
|
|
514
|
+
return q.recommended.map((id) => q.options.find((o) => o.id === id)?.label ?? id).join('; ');
|
|
515
|
+
return String(q.recommended) + (q.unit ? ' ' + q.unit : '');
|
|
516
|
+
}
|
|
517
|
+
/** A short line for the agent: `id: answer — note: "…"` (with flags). */
|
|
518
|
+
export function compactLine(q, id, a, hidden = false) {
|
|
519
|
+
const flags = [hidden ? 'now hidden' : '', q && isStale(q, a) ? 'question changed since' : '', !q ? 'question removed' : ''].filter(Boolean);
|
|
520
|
+
const note = a.note?.trim() ? ` — note: ${JSON.stringify(a.note.trim())}` : '';
|
|
521
|
+
return `- ${id}: ${answerText(q, a)}${note}${flags.length ? ` [${flags.join(', ')}]` : ''}`;
|
|
522
|
+
}
|
|
523
|
+
/** The answers as a readable markdown file, grouped by section. */
|
|
524
|
+
export function answersMarkdown(set, af) {
|
|
525
|
+
const vis = visibility(set, af.answers);
|
|
526
|
+
const s = summarise(set, af);
|
|
527
|
+
const out = [`# ${set.title}: answers`, '', `${s.done} of ${s.visible} answered${s.blocking ? `, ${s.blocking} blocking still open` : ''}. Written by questionme; the answers themselves are in the .json next to this file.`, ''];
|
|
528
|
+
for (const sec of set.sections) {
|
|
529
|
+
const rows = sec.questions.filter((q) => af.answers[q.id] || af.threads[q.id]?.length);
|
|
530
|
+
if (!rows.length)
|
|
531
|
+
continue;
|
|
532
|
+
if (sec.title)
|
|
533
|
+
out.push(`## ${sec.title}`, '');
|
|
534
|
+
for (const q of rows) {
|
|
535
|
+
const a = af.answers[q.id];
|
|
536
|
+
out.push(`### ${q.id}: ${q.question.replace(/\n+/g, ' ')}`);
|
|
537
|
+
if (a) {
|
|
538
|
+
out.push('', answerText(q, a));
|
|
539
|
+
if (a.note?.trim())
|
|
540
|
+
out.push('', '> ' + a.note.trim().replace(/\n/g, '\n> '));
|
|
541
|
+
const flags = [!vis.question(q.id) ? 'now hidden' : '', isStale(q, a) ? 'the question changed after this answer' : ''].filter(Boolean);
|
|
542
|
+
if (flags.length)
|
|
543
|
+
out.push('', `_(${flags.join('; ')})_`);
|
|
544
|
+
if (a.ack)
|
|
545
|
+
out.push('', `Applied: ${a.ack.note || 'yes'}`);
|
|
546
|
+
}
|
|
547
|
+
for (const m of af.threads[q.id] ?? [])
|
|
548
|
+
out.push('', `- **${m.from === 'owner' ? 'Owner' : 'Agent'}:** ${m.body.replace(/\n/g, ' ')}`);
|
|
549
|
+
out.push('');
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
const orphans = Object.keys(af.answers).filter((id) => !questionById(set, id));
|
|
553
|
+
if (orphans.length) {
|
|
554
|
+
out.push('## Answers to removed questions', '');
|
|
555
|
+
for (const id of orphans) {
|
|
556
|
+
const a = af.answers[id];
|
|
557
|
+
if (a)
|
|
558
|
+
out.push(compactLine(undefined, id, a));
|
|
559
|
+
}
|
|
560
|
+
out.push('');
|
|
561
|
+
}
|
|
562
|
+
return out.join('\n');
|
|
563
|
+
}
|
|
564
|
+
/** A clean answer from what the page sent, or an error. */
|
|
565
|
+
export function checkAnswer(q, input) {
|
|
566
|
+
const status = input.status && STATUSES.includes(input.status) ? input.status : 'answered';
|
|
567
|
+
const out = { status };
|
|
568
|
+
const note = typeof input.note === 'string' ? input.note.slice(0, 20000) : '';
|
|
569
|
+
if (note.trim())
|
|
570
|
+
out.note = note;
|
|
571
|
+
if (status !== 'answered')
|
|
572
|
+
return { ok: true, answer: out };
|
|
573
|
+
const ids = new Set(q.options.map((o) => o.id));
|
|
574
|
+
if (q.type === 'single' || q.type === 'multi' || q.type === 'yesno') {
|
|
575
|
+
const picked = Array.isArray(input.picked) ? [...new Set(input.picked.map(String))] : [];
|
|
576
|
+
for (const p of picked)
|
|
577
|
+
if (!ids.has(p))
|
|
578
|
+
return { ok: false, error: `"${p}" isn't an option` };
|
|
579
|
+
if (q.type !== 'multi' && picked.length > 1)
|
|
580
|
+
return { ok: false, error: 'Pick one' };
|
|
581
|
+
const other = typeof input.other === 'string' ? input.other.slice(0, 20000) : '';
|
|
582
|
+
if (other.trim() && !q.allowOther)
|
|
583
|
+
return { ok: false, error: 'This question has no "Other" answer' };
|
|
584
|
+
if (q.type === 'single' && picked.length && other.trim())
|
|
585
|
+
return { ok: false, error: 'Pick one, or write your own' };
|
|
586
|
+
if (picked.length) {
|
|
587
|
+
out.picked = picked;
|
|
588
|
+
out.labels = picked.map((p) => q.options.find((o) => o.id === p)?.label ?? p);
|
|
589
|
+
}
|
|
590
|
+
if (other.trim())
|
|
591
|
+
out.other = other;
|
|
592
|
+
if (!picked.length && !other.trim() && !note.trim())
|
|
593
|
+
return { ok: false, error: 'Nothing picked' };
|
|
594
|
+
}
|
|
595
|
+
else if (q.type === 'text') {
|
|
596
|
+
const text = typeof input.text === 'string' ? input.text.slice(0, 100000) : '';
|
|
597
|
+
if (!text.trim() && !note.trim())
|
|
598
|
+
return { ok: false, error: 'Nothing written' };
|
|
599
|
+
out.text = text;
|
|
600
|
+
}
|
|
601
|
+
else if (q.type === 'number' || q.type === 'scale') {
|
|
602
|
+
const n = typeof input.number === 'number' && Number.isFinite(input.number) ? input.number : undefined;
|
|
603
|
+
if (n === undefined)
|
|
604
|
+
return { ok: false, error: 'Give a number' };
|
|
605
|
+
if (q.min !== undefined && n < q.min)
|
|
606
|
+
return { ok: false, error: `At least ${q.min}` };
|
|
607
|
+
if (q.max !== undefined && n > q.max)
|
|
608
|
+
return { ok: false, error: `At most ${q.max}` };
|
|
609
|
+
out.number = n;
|
|
610
|
+
}
|
|
611
|
+
else if (q.type === 'rank') {
|
|
612
|
+
const order = Array.isArray(input.order) ? input.order.map(String) : [];
|
|
613
|
+
if (order.length !== ids.size || new Set(order).size !== order.length || order.some((o) => !ids.has(o)))
|
|
614
|
+
return { ok: false, error: 'Rank every option once' };
|
|
615
|
+
out.order = order;
|
|
616
|
+
}
|
|
617
|
+
return { ok: true, answer: out };
|
|
618
|
+
}
|
|
619
|
+
/** The answer "accept the recommendation" gives, or undefined when there is none. */
|
|
620
|
+
export function recommendedAnswer(q) {
|
|
621
|
+
const r = q.recommended;
|
|
622
|
+
if (r === undefined)
|
|
623
|
+
return undefined;
|
|
624
|
+
if (q.type === 'number' || q.type === 'scale')
|
|
625
|
+
return typeof r === 'number' ? { status: 'answered', number: r } : undefined;
|
|
626
|
+
if (q.type === 'text')
|
|
627
|
+
return typeof r === 'string' && r ? { status: 'answered', text: r } : undefined;
|
|
628
|
+
if (q.type === 'rank')
|
|
629
|
+
return Array.isArray(r) && r.length === q.options.length ? { status: 'answered', order: r } : undefined;
|
|
630
|
+
if (Array.isArray(r) && r.length)
|
|
631
|
+
return { status: 'answered', picked: q.type === 'multi' ? r : r.slice(0, 1) };
|
|
632
|
+
return undefined;
|
|
633
|
+
}
|