@alisio/plugin-thesis 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/.agents/agents/thesis-architect.md +38 -0
- package/.agents/agents/thesis-coordinator.md +39 -0
- package/.agents/agents/thesis-editor.md +35 -0
- package/.agents/agents/thesis-evidence-auditor.md +37 -0
- package/.agents/agents/thesis-librarian.md +38 -0
- package/.agents/agents/thesis-methodologist.md +39 -0
- package/.agents/agents/thesis-reviewer.md +36 -0
- package/.agents/agents/thesis-writer.md +38 -0
- package/.agents/skills/thesis-build/SKILL.md +48 -0
- package/.agents/skills/thesis-citations/SKILL.md +49 -0
- package/.agents/skills/thesis-csl-authoring/SKILL.md +48 -0
- package/.agents/skills/thesis-editing/SKILL.md +49 -0
- package/.agents/skills/thesis-evidence/SKILL.md +48 -0
- package/.agents/skills/thesis-figures/SKILL.md +50 -0
- package/.agents/skills/thesis-institution-norms/SKILL.md +50 -0
- package/.agents/skills/thesis-intake/SKILL.md +48 -0
- package/.agents/skills/thesis-math/SKILL.md +48 -0
- package/.agents/skills/thesis-methodology/SKILL.md +49 -0
- package/.agents/skills/thesis-outline/SKILL.md +48 -0
- package/.agents/skills/thesis-policy/SKILL.md +48 -0
- package/.agents/skills/thesis-review/SKILL.md +49 -0
- package/.agents/skills/thesis-search/SKILL.md +47 -0
- package/.agents/skills/thesis-writing/SKILL.md +52 -0
- package/LICENSE +21 -0
- package/README.es.md +242 -0
- package/README.md +235 -0
- package/THIRD_PARTY_NOTICES.md +94 -0
- package/assets/architecture.svg +1 -0
- package/assets/evidence-pipeline.svg +1 -0
- package/assets/lifecycle.svg +1 -0
- package/assets/policy-resolution.svg +1 -0
- package/cover.webp +0 -0
- package/dist/authoring.d.ts +30 -0
- package/dist/authoring.d.ts.map +1 -0
- package/dist/authoring.js +681 -0
- package/dist/authoring.js.map +1 -0
- package/dist/brief.d.ts +40 -0
- package/dist/brief.d.ts.map +1 -0
- package/dist/brief.js +566 -0
- package/dist/brief.js.map +1 -0
- package/dist/cache.d.ts +8 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +20 -0
- package/dist/cache.js.map +1 -0
- package/dist/checks/brief.d.ts +5 -0
- package/dist/checks/brief.d.ts.map +1 -0
- package/dist/checks/brief.js +38 -0
- package/dist/checks/brief.js.map +1 -0
- package/dist/checks/citations.d.ts +14 -0
- package/dist/checks/citations.d.ts.map +1 -0
- package/dist/checks/citations.js +87 -0
- package/dist/checks/citations.js.map +1 -0
- package/dist/checks/claims.d.ts +16 -0
- package/dist/checks/claims.d.ts.map +1 -0
- package/dist/checks/claims.js +147 -0
- package/dist/checks/claims.js.map +1 -0
- package/dist/checks/closing.d.ts +20 -0
- package/dist/checks/closing.d.ts.map +1 -0
- package/dist/checks/closing.js +168 -0
- package/dist/checks/closing.js.map +1 -0
- package/dist/checks/design.d.ts +5 -0
- package/dist/checks/design.d.ts.map +1 -0
- package/dist/checks/design.js +107 -0
- package/dist/checks/design.js.map +1 -0
- package/dist/checks/document.d.ts +17 -0
- package/dist/checks/document.d.ts.map +1 -0
- package/dist/checks/document.js +213 -0
- package/dist/checks/document.js.map +1 -0
- package/dist/checks/evidence.d.ts +7 -0
- package/dist/checks/evidence.d.ts.map +1 -0
- package/dist/checks/evidence.js +133 -0
- package/dist/checks/evidence.js.map +1 -0
- package/dist/checks/hygiene.d.ts +5 -0
- package/dist/checks/hygiene.d.ts.map +1 -0
- package/dist/checks/hygiene.js +37 -0
- package/dist/checks/hygiene.js.map +1 -0
- package/dist/checks/index.d.ts +21 -0
- package/dist/checks/index.d.ts.map +1 -0
- package/dist/checks/index.js +98 -0
- package/dist/checks/index.js.map +1 -0
- package/dist/checks/language.d.ts +14 -0
- package/dist/checks/language.d.ts.map +1 -0
- package/dist/checks/language.js +73 -0
- package/dist/checks/language.js.map +1 -0
- package/dist/checks/outline.d.ts +25 -0
- package/dist/checks/outline.d.ts.map +1 -0
- package/dist/checks/outline.js +214 -0
- package/dist/checks/outline.js.map +1 -0
- package/dist/checks/policy.d.ts +5 -0
- package/dist/checks/policy.d.ts.map +1 -0
- package/dist/checks/policy.js +129 -0
- package/dist/checks/policy.js.map +1 -0
- package/dist/checks/project.d.ts +65 -0
- package/dist/checks/project.d.ts.map +1 -0
- package/dist/checks/project.js +203 -0
- package/dist/checks/project.js.map +1 -0
- package/dist/checks/styles.d.ts +12 -0
- package/dist/checks/styles.d.ts.map +1 -0
- package/dist/checks/styles.js +67 -0
- package/dist/checks/styles.js.map +1 -0
- package/dist/checks/writing.d.ts +6 -0
- package/dist/checks/writing.d.ts.map +1 -0
- package/dist/checks/writing.js +115 -0
- package/dist/checks/writing.js.map +1 -0
- package/dist/chrome/detect.d.ts +19 -0
- package/dist/chrome/detect.d.ts.map +1 -0
- package/dist/chrome/detect.js +116 -0
- package/dist/chrome/detect.js.map +1 -0
- package/dist/claims.d.ts +19 -0
- package/dist/claims.d.ts.map +1 -0
- package/dist/claims.js +99 -0
- package/dist/claims.js.map +1 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +262 -0
- package/dist/cli.js.map +1 -0
- package/dist/context.d.ts +55 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +62 -0
- package/dist/context.js.map +1 -0
- package/dist/coordinator.d.ts +139 -0
- package/dist/coordinator.d.ts.map +1 -0
- package/dist/coordinator.js +710 -0
- package/dist/coordinator.js.map +1 -0
- package/dist/delegate.d.ts +34 -0
- package/dist/delegate.d.ts.map +1 -0
- package/dist/delegate.js +105 -0
- package/dist/delegate.js.map +1 -0
- package/dist/diffguard.d.ts +24 -0
- package/dist/diffguard.d.ts.map +1 -0
- package/dist/diffguard.js +92 -0
- package/dist/diffguard.js.map +1 -0
- package/dist/doctor.d.ts +34 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +155 -0
- package/dist/doctor.js.map +1 -0
- package/dist/draft.d.ts +64 -0
- package/dist/draft.d.ts.map +1 -0
- package/dist/draft.js +801 -0
- package/dist/draft.js.map +1 -0
- package/dist/figure.d.ts +13 -0
- package/dist/figure.d.ts.map +1 -0
- package/dist/figure.js +143 -0
- package/dist/figure.js.map +1 -0
- package/dist/finalize.d.ts +29 -0
- package/dist/finalize.d.ts.map +1 -0
- package/dist/finalize.js +230 -0
- package/dist/finalize.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +206 -0
- package/dist/index.js.map +1 -0
- package/dist/intake.d.ts +57 -0
- package/dist/intake.d.ts.map +1 -0
- package/dist/intake.js +772 -0
- package/dist/intake.js.map +1 -0
- package/dist/labels.d.ts +50 -0
- package/dist/labels.d.ts.map +1 -0
- package/dist/labels.js +98 -0
- package/dist/labels.js.map +1 -0
- package/dist/outline.d.ts +45 -0
- package/dist/outline.d.ts.map +1 -0
- package/dist/outline.js +148 -0
- package/dist/outline.js.map +1 -0
- package/dist/pack-commands.d.ts +18 -0
- package/dist/pack-commands.d.ts.map +1 -0
- package/dist/pack-commands.js +397 -0
- package/dist/pack-commands.js.map +1 -0
- package/dist/package-paths.d.ts +8 -0
- package/dist/package-paths.d.ts.map +1 -0
- package/dist/package-paths.js +13 -0
- package/dist/package-paths.js.map +1 -0
- package/dist/policy/context.d.ts +19 -0
- package/dist/policy/context.d.ts.map +1 -0
- package/dist/policy/context.js +62 -0
- package/dist/policy/context.js.map +1 -0
- package/dist/policy/ethics.d.ts +59 -0
- package/dist/policy/ethics.d.ts.map +1 -0
- package/dist/policy/ethics.js +106 -0
- package/dist/policy/ethics.js.map +1 -0
- package/dist/policy/kinds.d.ts +9 -0
- package/dist/policy/kinds.d.ts.map +1 -0
- package/dist/policy/kinds.js +68 -0
- package/dist/policy/kinds.js.map +1 -0
- package/dist/policy/packs.d.ts +53 -0
- package/dist/policy/packs.d.ts.map +1 -0
- package/dist/policy/packs.js +336 -0
- package/dist/policy/packs.js.map +1 -0
- package/dist/policy/resolver.d.ts +87 -0
- package/dist/policy/resolver.d.ts.map +1 -0
- package/dist/policy/resolver.js +337 -0
- package/dist/policy/resolver.js.map +1 -0
- package/dist/policy/rules.d.ts +65 -0
- package/dist/policy/rules.d.ts.map +1 -0
- package/dist/policy/rules.js +194 -0
- package/dist/policy/rules.js.map +1 -0
- package/dist/protocol.d.ts +46 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +86 -0
- package/dist/protocol.js.map +1 -0
- package/dist/render/adapters/chrome-pdf/cdp.d.ts +36 -0
- package/dist/render/adapters/chrome-pdf/cdp.d.ts.map +1 -0
- package/dist/render/adapters/chrome-pdf/cdp.js +291 -0
- package/dist/render/adapters/chrome-pdf/cdp.js.map +1 -0
- package/dist/render/adapters/chrome-pdf/index.d.ts +10 -0
- package/dist/render/adapters/chrome-pdf/index.d.ts.map +1 -0
- package/dist/render/adapters/chrome-pdf/index.js +139 -0
- package/dist/render/adapters/chrome-pdf/index.js.map +1 -0
- package/dist/render/adapters/chrome-pdf/network.d.ts +7 -0
- package/dist/render/adapters/chrome-pdf/network.d.ts.map +1 -0
- package/dist/render/adapters/chrome-pdf/network.js +33 -0
- package/dist/render/adapters/chrome-pdf/network.js.map +1 -0
- package/dist/render/adapters/html-preview/index.d.ts +8 -0
- package/dist/render/adapters/html-preview/index.d.ts.map +1 -0
- package/dist/render/adapters/html-preview/index.js +62 -0
- package/dist/render/adapters/html-preview/index.js.map +1 -0
- package/dist/render/adapters/test-json/index.d.ts +8 -0
- package/dist/render/adapters/test-json/index.d.ts.map +1 -0
- package/dist/render/adapters/test-json/index.js +62 -0
- package/dist/render/adapters/test-json/index.js.map +1 -0
- package/dist/render/adapters/typst-pdf/emitter.d.ts +21 -0
- package/dist/render/adapters/typst-pdf/emitter.d.ts.map +1 -0
- package/dist/render/adapters/typst-pdf/emitter.js +259 -0
- package/dist/render/adapters/typst-pdf/emitter.js.map +1 -0
- package/dist/render/adapters/typst-pdf/escape.d.ts +14 -0
- package/dist/render/adapters/typst-pdf/escape.d.ts.map +1 -0
- package/dist/render/adapters/typst-pdf/escape.js +56 -0
- package/dist/render/adapters/typst-pdf/escape.js.map +1 -0
- package/dist/render/adapters/typst-pdf/index.d.ts +9 -0
- package/dist/render/adapters/typst-pdf/index.d.ts.map +1 -0
- package/dist/render/adapters/typst-pdf/index.js +154 -0
- package/dist/render/adapters/typst-pdf/index.js.map +1 -0
- package/dist/render/adapters/typst-pdf/profile.d.ts +3 -0
- package/dist/render/adapters/typst-pdf/profile.d.ts.map +1 -0
- package/dist/render/adapters/typst-pdf/profile.js +55 -0
- package/dist/render/adapters/typst-pdf/profile.js.map +1 -0
- package/dist/render/adapters/typst-pdf/runner.d.ts +52 -0
- package/dist/render/adapters/typst-pdf/runner.d.ts.map +1 -0
- package/dist/render/adapters/typst-pdf/runner.js +209 -0
- package/dist/render/adapters/typst-pdf/runner.js.map +1 -0
- package/dist/render/adapters/typst-pdf/setup.d.ts +30 -0
- package/dist/render/adapters/typst-pdf/setup.d.ts.map +1 -0
- package/dist/render/adapters/typst-pdf/setup.js +183 -0
- package/dist/render/adapters/typst-pdf/setup.js.map +1 -0
- package/dist/render/adapters/typst-pdf/version.d.ts +8 -0
- package/dist/render/adapters/typst-pdf/version.d.ts.map +1 -0
- package/dist/render/adapters/typst-pdf/version.js +14 -0
- package/dist/render/adapters/typst-pdf/version.js.map +1 -0
- package/dist/render/assemble.d.ts +28 -0
- package/dist/render/assemble.d.ts.map +1 -0
- package/dist/render/assemble.js +309 -0
- package/dist/render/assemble.js.map +1 -0
- package/dist/render/assets.d.ts +27 -0
- package/dist/render/assets.d.ts.map +1 -0
- package/dist/render/assets.js +232 -0
- package/dist/render/assets.js.map +1 -0
- package/dist/render/build.d.ts +43 -0
- package/dist/render/build.d.ts.map +1 -0
- package/dist/render/build.js +187 -0
- package/dist/render/build.js.map +1 -0
- package/dist/render/charts.d.ts +83 -0
- package/dist/render/charts.d.ts.map +1 -0
- package/dist/render/charts.js +633 -0
- package/dist/render/charts.js.map +1 -0
- package/dist/render/default-registry.d.ts +7 -0
- package/dist/render/default-registry.d.ts.map +1 -0
- package/dist/render/default-registry.js +15 -0
- package/dist/render/default-registry.js.map +1 -0
- package/dist/render/diagram-theme.d.ts +7 -0
- package/dist/render/diagram-theme.d.ts.map +1 -0
- package/dist/render/diagram-theme.js +50 -0
- package/dist/render/diagram-theme.js.map +1 -0
- package/dist/render/dialect.d.ts +23 -0
- package/dist/render/dialect.d.ts.map +1 -0
- package/dist/render/dialect.js +188 -0
- package/dist/render/dialect.js.map +1 -0
- package/dist/render/html/citations.d.ts +70 -0
- package/dist/render/html/citations.d.ts.map +1 -0
- package/dist/render/html/citations.js +371 -0
- package/dist/render/html/citations.js.map +1 -0
- package/dist/render/html/css.d.ts +14 -0
- package/dist/render/html/css.d.ts.map +1 -0
- package/dist/render/html/css.js +113 -0
- package/dist/render/html/css.js.map +1 -0
- package/dist/render/html/emitter.d.ts +20 -0
- package/dist/render/html/emitter.d.ts.map +1 -0
- package/dist/render/html/emitter.js +652 -0
- package/dist/render/html/emitter.js.map +1 -0
- package/dist/render/html/escape.d.ts +11 -0
- package/dist/render/html/escape.d.ts.map +1 -0
- package/dist/render/html/escape.js +32 -0
- package/dist/render/html/escape.js.map +1 -0
- package/dist/render/html/katex.d.ts +9 -0
- package/dist/render/html/katex.d.ts.map +1 -0
- package/dist/render/html/katex.js +39 -0
- package/dist/render/html/katex.js.map +1 -0
- package/dist/render/html/stage.d.ts +12 -0
- package/dist/render/html/stage.d.ts.map +1 -0
- package/dist/render/html/stage.js +30 -0
- package/dist/render/html/stage.js.map +1 -0
- package/dist/render/index.d.ts +5 -0
- package/dist/render/index.d.ts.map +1 -0
- package/dist/render/index.js +5 -0
- package/dist/render/index.js.map +1 -0
- package/dist/render/math.d.ts +9 -0
- package/dist/render/math.d.ts.map +1 -0
- package/dist/render/math.js +60 -0
- package/dist/render/math.js.map +1 -0
- package/dist/render/model.d.ts +233 -0
- package/dist/render/model.d.ts.map +1 -0
- package/dist/render/model.js +2 -0
- package/dist/render/model.js.map +1 -0
- package/dist/render/numbering.d.ts +51 -0
- package/dist/render/numbering.d.ts.map +1 -0
- package/dist/render/numbering.js +155 -0
- package/dist/render/numbering.js.map +1 -0
- package/dist/render/output-name.d.ts +4 -0
- package/dist/render/output-name.d.ts.map +1 -0
- package/dist/render/output-name.js +11 -0
- package/dist/render/output-name.js.map +1 -0
- package/dist/render/palettes.d.ts +22 -0
- package/dist/render/palettes.d.ts.map +1 -0
- package/dist/render/palettes.js +46 -0
- package/dist/render/palettes.js.map +1 -0
- package/dist/render/parse.d.ts +15 -0
- package/dist/render/parse.d.ts.map +1 -0
- package/dist/render/parse.js +637 -0
- package/dist/render/parse.js.map +1 -0
- package/dist/render/port.d.ts +79 -0
- package/dist/render/port.d.ts.map +1 -0
- package/dist/render/port.js +2 -0
- package/dist/render/port.js.map +1 -0
- package/dist/render/registry.d.ts +11 -0
- package/dist/render/registry.d.ts.map +1 -0
- package/dist/render/registry.js +21 -0
- package/dist/render/registry.js.map +1 -0
- package/dist/render/text.d.ts +13 -0
- package/dist/render/text.d.ts.map +1 -0
- package/dist/render/text.js +65 -0
- package/dist/render/text.js.map +1 -0
- package/dist/render/walk.d.ts +13 -0
- package/dist/render/walk.d.ts.map +1 -0
- package/dist/render/walk.js +69 -0
- package/dist/render/walk.js.map +1 -0
- package/dist/research/bibtex.d.ts +12 -0
- package/dist/research/bibtex.d.ts.map +1 -0
- package/dist/research/bibtex.js +99 -0
- package/dist/research/bibtex.js.map +1 -0
- package/dist/research/client.d.ts +112 -0
- package/dist/research/client.d.ts.map +1 -0
- package/dist/research/client.js +661 -0
- package/dist/research/client.js.map +1 -0
- package/dist/research/dossier.d.ts +31 -0
- package/dist/research/dossier.d.ts.map +1 -0
- package/dist/research/dossier.js +54 -0
- package/dist/research/dossier.js.map +1 -0
- package/dist/research/flow.d.ts +31 -0
- package/dist/research/flow.d.ts.map +1 -0
- package/dist/research/flow.js +635 -0
- package/dist/research/flow.js.map +1 -0
- package/dist/research/library.d.ts +79 -0
- package/dist/research/library.d.ts.map +1 -0
- package/dist/research/library.js +319 -0
- package/dist/research/library.js.map +1 -0
- package/dist/research/verify.d.ts +80 -0
- package/dist/research/verify.d.ts.map +1 -0
- package/dist/research/verify.js +307 -0
- package/dist/research/verify.js.map +1 -0
- package/dist/resources.d.ts +15 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +148 -0
- package/dist/resources.js.map +1 -0
- package/dist/review.d.ts +22 -0
- package/dist/review.d.ts.map +1 -0
- package/dist/review.js +355 -0
- package/dist/review.js.map +1 -0
- package/dist/schemas.d.ts +252 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +1020 -0
- package/dist/schemas.js.map +1 -0
- package/dist/sections.d.ts +30 -0
- package/dist/sections.d.ts.map +1 -0
- package/dist/sections.js +62 -0
- package/dist/sections.js.map +1 -0
- package/dist/storage.d.ts +21 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +296 -0
- package/dist/storage.js.map +1 -0
- package/dist/style-commands.d.ts +15 -0
- package/dist/style-commands.d.ts.map +1 -0
- package/dist/style-commands.js +54 -0
- package/dist/style-commands.js.map +1 -0
- package/dist/styles/csl.d.ts +58 -0
- package/dist/styles/csl.d.ts.map +1 -0
- package/dist/styles/csl.js +112 -0
- package/dist/styles/csl.js.map +1 -0
- package/dist/styles/discovery.d.ts +45 -0
- package/dist/styles/discovery.d.ts.map +1 -0
- package/dist/styles/discovery.js +99 -0
- package/dist/styles/discovery.js.map +1 -0
- package/dist/styles/golden.d.ts +52 -0
- package/dist/styles/golden.d.ts.map +1 -0
- package/dist/styles/golden.js +216 -0
- package/dist/styles/golden.js.map +1 -0
- package/dist/styles/profile.d.ts +126 -0
- package/dist/styles/profile.d.ts.map +1 -0
- package/dist/styles/profile.js +582 -0
- package/dist/styles/profile.js.map +1 -0
- package/dist/styles/xml.d.ts +22 -0
- package/dist/styles/xml.d.ts.map +1 -0
- package/dist/styles/xml.js +179 -0
- package/dist/styles/xml.js.map +1 -0
- package/dist/types.d.ts +285 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +155 -0
- package/dist/types.js.map +1 -0
- package/dist/typst-pins.d.ts +16 -0
- package/dist/typst-pins.d.ts.map +1 -0
- package/dist/typst-pins.js +39 -0
- package/dist/typst-pins.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/dist/workflow.d.ts +49 -0
- package/dist/workflow.d.ts.map +1 -0
- package/dist/workflow.js +722 -0
- package/dist/workflow.js.map +1 -0
- package/dist/workspace.d.ts +12 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +71 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +70 -0
- package/policy-packs/countries/CO/academic-standards/ntc-1486-2022.yaml +188 -0
- package/policy-packs/countries/CO/copyright/decision-andina-351.yaml +61 -0
- package/policy-packs/countries/CO/copyright/ley-1915-2018.yaml +25 -0
- package/policy-packs/countries/CO/copyright/ley-23-1982.yaml +64 -0
- package/policy-packs/countries/CO/domains/health/resolucion-8430-1993.yaml +219 -0
- package/policy-packs/countries/CO/higher-education/ley-30-1992.yaml +61 -0
- package/policy-packs/countries/CO/manifest.yaml +42 -0
- package/policy-packs/countries/CO/official-domains.yaml +90 -0
- package/policy-packs/countries/CO/privacy/ley-1581-2012.yaml +197 -0
- package/policy-packs/countries/CO/research-integrity/minciencias-res-0314-2018.yaml +64 -0
- package/policy-packs/global/academic-integrity.yaml +45 -0
- package/policy-packs/global/academic-writing.yaml +70 -0
- package/policy-packs/global/evidence-quality.yaml +81 -0
- package/policy-packs/global/manifest.yaml +54 -0
- package/policy-packs/global/objective-verbs.yaml +245 -0
- package/policy-packs/global/reporting-guidelines.yaml +392 -0
- package/policy-packs/global/work-types.yaml +195 -0
- package/styles/LICENSE-CSL +375 -0
- package/styles/apa.csl +2283 -0
- package/styles/catalog.json +43 -0
- package/styles/fixtures/apa-7.expected.json +25 -0
- package/styles/fixtures/icontec-ntc1486-2022.expected.json +25 -0
- package/styles/fixtures/ieee.expected.json +25 -0
- package/styles/fixtures/references.jsonl +8 -0
- package/styles/icontec-ntc1486-2022.csl +388 -0
- package/styles/ieee.csl +519 -0
- package/templates/html/runtime.js +159 -0
- package/templates/html/thesis.css +98 -0
- package/templates/html/vendor/katex/LICENSE +21 -0
- package/templates/html/vendor/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- package/templates/html/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/templates/html/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/templates/html/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/templates/html/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/templates/html/vendor/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
- package/templates/html/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/templates/html/vendor/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
- package/templates/html/vendor/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
- package/templates/html/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/templates/html/vendor/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
- package/templates/html/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/templates/html/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/templates/html/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/templates/html/vendor/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
- package/templates/html/vendor/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- package/templates/html/vendor/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- package/templates/html/vendor/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- package/templates/html/vendor/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- package/templates/html/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/templates/html/vendor/katex/katex.min.css +1 -0
- package/templates/html/vendor/katex/katex.min.js +1 -0
- package/templates/html/vendor/mermaid/LICENSE +21 -0
- package/templates/html/vendor/mermaid/mermaid.min.js +3587 -0
- package/templates/html/vendor/pagedjs/LICENSE +21 -0
- package/templates/html/vendor/pagedjs/paged.polyfill.min.js +4 -0
- package/templates/palettes.json +57 -0
- package/templates/profiles/apa-7.profile.yaml +15 -0
- package/templates/profiles/generic.profile.yaml +20 -0
- package/templates/profiles/icontec-ntc1486-2022.profile.yaml +36 -0
- package/templates/profiles/ieee.profile.yaml +14 -0
- package/templates/typst/i18n/en.yaml +31 -0
- package/templates/typst/i18n/es.yaml +31 -0
- package/templates/typst/i18n/pt.yaml +31 -0
- package/templates/typst/thesis.typ +276 -0
- package/typst-packages/preview/merman/0.3.0/LICENSE +231 -0
- package/typst-packages/preview/merman/0.3.0/README.md +388 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/cose-base-v1/LICENSE +21 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/cose-base-v2/LICENSE +21 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/cytoscape/LICENSE +19 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/cytoscape-cose-bilkent/LICENSE +21 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/cytoscape-fcose/LICENSE +19 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/d3-shape/LICENSE +13 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/dagre/LICENSE +19 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/dompurify/LICENSE +202 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/eclipse-elk/LICENSE.md +264 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/elkjs/LICENSE.md +264 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/fmin/LICENSE +27 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/graphlib/LICENSE +19 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/layout-base-v1/LICENSE +21 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/layout-base-v2/LICENSE +21 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/mermaid/LICENSE +21 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/rough-rs/LICENSE +21 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/roughjs/LICENSE +21 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/sanitize-url/LICENSE +21 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/venn-js/LICENSE +22 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/wasm-minimal-protocol/LICENSE +24 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/zenuml-core/LICENSE +21 -0
- package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_NOTICES.md +265 -0
- package/typst-packages/preview/merman/0.3.0/lib.typ +1 -0
- package/typst-packages/preview/merman/0.3.0/merman_typst_plugin.wasm +0 -0
- package/typst-packages/preview/merman/0.3.0/src/capabilities.typ +5 -0
- package/typst-packages/preview/merman/0.3.0/src/context.typ +1 -0
- package/typst-packages/preview/merman/0.3.0/src/errors.typ +33 -0
- package/typst-packages/preview/merman/0.3.0/src/exports.typ +5 -0
- package/typst-packages/preview/merman/0.3.0/src/figure.typ +143 -0
- package/typst-packages/preview/merman/0.3.0/src/image.typ +50 -0
- package/typst-packages/preview/merman/0.3.0/src/options.typ +566 -0
- package/typst-packages/preview/merman/0.3.0/src/plugin.typ +1 -0
- package/typst-packages/preview/merman/0.3.0/src/raw.typ +22 -0
- package/typst-packages/preview/merman/0.3.0/src/render.typ +137 -0
- package/typst-packages/preview/merman/0.3.0/src/source.typ +7 -0
- package/typst-packages/preview/merman/0.3.0/src/units.typ +51 -0
- package/typst-packages/preview/merman/0.3.0/typst.toml +12 -0
- package/typst-packages/preview/mitex/0.2.7/CHANGELOG.md +69 -0
- package/typst-packages/preview/mitex/0.2.7/LICENSE +176 -0
- package/typst-packages/preview/mitex/0.2.7/README.md +154 -0
- package/typst-packages/preview/mitex/0.2.7/lib.typ +1 -0
- package/typst-packages/preview/mitex/0.2.7/mitex.typ +45 -0
- package/typst-packages/preview/mitex/0.2.7/mitex.wasm +0 -0
- package/typst-packages/preview/mitex/0.2.7/specs/README.md +250 -0
- package/typst-packages/preview/mitex/0.2.7/specs/latex/standard.typ +1187 -0
- package/typst-packages/preview/mitex/0.2.7/specs/mod.typ +12 -0
- package/typst-packages/preview/mitex/0.2.7/specs/prelude.typ +230 -0
- package/typst-packages/preview/mitex/0.2.7/typst.toml +12 -0
package/dist/schemas.js
ADDED
|
@@ -0,0 +1,1020 @@
|
|
|
1
|
+
import { stringify as stringifyYaml } from "yaml";
|
|
2
|
+
import { approaches, claimKinds, evidenceStatuses, evidenceTypes, permittedUses, reviewCategories, reviewRoutes, routeOfCategory, } from "./types.js";
|
|
3
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: control characters are what is stripped
|
|
4
|
+
const controlPattern = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g;
|
|
5
|
+
/** Child output is untrusted: remove control characters before any further use. */
|
|
6
|
+
export function stripControl(text) {
|
|
7
|
+
return text.replace(controlPattern, "");
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Parse a child's reply: a strict JSON document, or exactly one fenced `json` block. Anything else
|
|
11
|
+
* (prose around the JSON, several blocks) is rejected so the coordinator can retry with the error.
|
|
12
|
+
*/
|
|
13
|
+
export function parseChildJson(text) {
|
|
14
|
+
const trimmed = text.trim();
|
|
15
|
+
if (!trimmed)
|
|
16
|
+
throw new Error("The child returned no output");
|
|
17
|
+
const fences = [...trimmed.matchAll(/```json\s*\n([\s\S]*?)\n```/g)];
|
|
18
|
+
let candidate = trimmed;
|
|
19
|
+
if (fences.length > 1)
|
|
20
|
+
throw new Error("The child returned more than one JSON block");
|
|
21
|
+
if (fences.length === 1) {
|
|
22
|
+
const [block, inner] = fences[0];
|
|
23
|
+
if (trimmed.replace(block, "").trim())
|
|
24
|
+
throw new Error("The child wrapped its JSON in extra prose");
|
|
25
|
+
candidate = inner.trim();
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
return JSON.parse(candidate);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
throw new Error("The child output is not valid JSON");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const domainPattern = /^[a-z][a-z0-9_]{1,40}$/;
|
|
35
|
+
export function validateIntakeDraft(value) {
|
|
36
|
+
const errors = [];
|
|
37
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
38
|
+
return { errors: ["IntakeDraft must be a JSON object"] };
|
|
39
|
+
}
|
|
40
|
+
const record = value;
|
|
41
|
+
for (const key of Object.keys(record)) {
|
|
42
|
+
if (!["titles", "domain", "approach", "notes"].includes(key))
|
|
43
|
+
errors.push(`Unknown field: ${key}`);
|
|
44
|
+
}
|
|
45
|
+
const titles = record.titles;
|
|
46
|
+
const cleanTitles = [];
|
|
47
|
+
if (!Array.isArray(titles) || titles.length < 1 || titles.length > 3) {
|
|
48
|
+
errors.push("titles must hold 1 to 3 working titles");
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
for (const title of titles) {
|
|
52
|
+
const clean = typeof title === "string" ? stripControl(title).trim() : "";
|
|
53
|
+
if (!clean || clean.length > 200)
|
|
54
|
+
errors.push("each title must be 1 to 200 characters");
|
|
55
|
+
else
|
|
56
|
+
cleanTitles.push(clean);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const draft = { titles: cleanTitles };
|
|
60
|
+
if (record.domain !== undefined) {
|
|
61
|
+
if (typeof record.domain === "string" && domainPattern.test(record.domain))
|
|
62
|
+
draft.domain = record.domain;
|
|
63
|
+
else
|
|
64
|
+
errors.push("domain must be a snake_case id");
|
|
65
|
+
}
|
|
66
|
+
if (record.approach !== undefined) {
|
|
67
|
+
if (typeof record.approach === "string" &&
|
|
68
|
+
approaches.includes(record.approach)) {
|
|
69
|
+
draft.approach = record.approach;
|
|
70
|
+
}
|
|
71
|
+
else
|
|
72
|
+
errors.push(`approach must be one of: ${approaches.join(", ")}`);
|
|
73
|
+
}
|
|
74
|
+
if (record.notes !== undefined) {
|
|
75
|
+
if (typeof record.notes === "string" && record.notes.length <= 1000)
|
|
76
|
+
draft.notes = stripControl(record.notes);
|
|
77
|
+
else
|
|
78
|
+
errors.push("notes must be a string of at most 1000 characters");
|
|
79
|
+
}
|
|
80
|
+
return errors.length > 0 ? { errors } : { value: draft, errors };
|
|
81
|
+
}
|
|
82
|
+
const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
83
|
+
function record(value, path, allowed, errors) {
|
|
84
|
+
if (!isObject(value)) {
|
|
85
|
+
errors.push(`${path} must be a JSON object`);
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
for (const key of Object.keys(value)) {
|
|
89
|
+
if (!allowed.includes(key))
|
|
90
|
+
errors.push(`${path}: unknown field ${key}`);
|
|
91
|
+
}
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
function text(source, key, path, errors, max, options = {}) {
|
|
95
|
+
const raw = source[key];
|
|
96
|
+
if (raw === undefined || raw === null) {
|
|
97
|
+
if (!options.optional)
|
|
98
|
+
errors.push(`${path}.${key} is required`);
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
if (typeof raw !== "string") {
|
|
102
|
+
errors.push(`${path}.${key} must be a string`);
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
const clean = stripControl(raw).trim();
|
|
106
|
+
if (clean.length < (options.min ?? 1) || clean.length > max) {
|
|
107
|
+
errors.push(`${path}.${key} must be ${options.min ?? 1} to ${max} characters (got ${clean.length})`);
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
return clean;
|
|
111
|
+
}
|
|
112
|
+
function list(source, key, path, errors, options) {
|
|
113
|
+
const raw = source[key];
|
|
114
|
+
if (raw === undefined && options.optional)
|
|
115
|
+
return [];
|
|
116
|
+
if (!Array.isArray(raw)) {
|
|
117
|
+
errors.push(`${path}.${key} must be a list`);
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
if (raw.length < (options.min ?? 0) || raw.length > options.max) {
|
|
121
|
+
errors.push(`${path}.${key} must hold ${options.min ?? 0} to ${options.max} items (got ${raw.length})`);
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
const out = [];
|
|
125
|
+
raw.forEach((entry, index) => {
|
|
126
|
+
const clean = typeof entry === "string" ? stripControl(entry).trim() : "";
|
|
127
|
+
if (!clean || clean.length > options.item) {
|
|
128
|
+
errors.push(`${path}.${key}[${index}] must be 1 to ${options.item} characters`);
|
|
129
|
+
}
|
|
130
|
+
else
|
|
131
|
+
out.push(clean);
|
|
132
|
+
});
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
function oneOf(source, key, path, errors, values, optional = false) {
|
|
136
|
+
const raw = source[key];
|
|
137
|
+
if (raw === undefined && optional)
|
|
138
|
+
return undefined;
|
|
139
|
+
if (typeof raw !== "string" || !values.includes(raw)) {
|
|
140
|
+
errors.push(`${path}.${key} must be one of: ${values.join(", ")}`);
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
return raw;
|
|
144
|
+
}
|
|
145
|
+
function integer(source, key, path, errors, min, max, optional = false) {
|
|
146
|
+
const raw = source[key];
|
|
147
|
+
if (raw === undefined && optional)
|
|
148
|
+
return undefined;
|
|
149
|
+
if (!Number.isInteger(raw) || raw < min || raw > max) {
|
|
150
|
+
errors.push(`${path}.${key} must be an integer from ${min} to ${max}`);
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
return raw;
|
|
154
|
+
}
|
|
155
|
+
const fold = (value) => value
|
|
156
|
+
.normalize("NFD")
|
|
157
|
+
.replace(/[̀-ͯ]/g, "")
|
|
158
|
+
.toLowerCase()
|
|
159
|
+
.replace(/[^a-z0-9]+/g, " ")
|
|
160
|
+
.trim();
|
|
161
|
+
function finish(errors, value) {
|
|
162
|
+
return errors.length > 0 ? { errors } : { value, errors: [] };
|
|
163
|
+
}
|
|
164
|
+
const ethicsIds = [
|
|
165
|
+
"human_participants",
|
|
166
|
+
"minors",
|
|
167
|
+
"identifiable_personal_data",
|
|
168
|
+
"sensitive_data",
|
|
169
|
+
"intervention",
|
|
170
|
+
"risk_above_minimal",
|
|
171
|
+
"biological_samples",
|
|
172
|
+
"animals",
|
|
173
|
+
"communities",
|
|
174
|
+
"clinical_research",
|
|
175
|
+
"additional_institutional_rules",
|
|
176
|
+
];
|
|
177
|
+
export function validateProtocolDraft(value, context) {
|
|
178
|
+
const errors = [];
|
|
179
|
+
const top = record(value, "ProtocolDraft", [
|
|
180
|
+
"problem",
|
|
181
|
+
"researchQuestions",
|
|
182
|
+
"generalObjective",
|
|
183
|
+
"specificObjectives",
|
|
184
|
+
"justification",
|
|
185
|
+
"scope",
|
|
186
|
+
"limitations",
|
|
187
|
+
"hypotheses",
|
|
188
|
+
"methodology",
|
|
189
|
+
"ethics",
|
|
190
|
+
"openQuestions",
|
|
191
|
+
], errors);
|
|
192
|
+
if (!top)
|
|
193
|
+
return { errors };
|
|
194
|
+
const problem = text(top, "problem", "ProtocolDraft", errors, 2000);
|
|
195
|
+
const researchQuestions = list(top, "researchQuestions", "ProtocolDraft", errors, {
|
|
196
|
+
min: 1,
|
|
197
|
+
max: 5,
|
|
198
|
+
item: 400,
|
|
199
|
+
});
|
|
200
|
+
const generalObjective = text(top, "generalObjective", "ProtocolDraft", errors, 400);
|
|
201
|
+
const specificObjectives = [];
|
|
202
|
+
if (!Array.isArray(top.specificObjectives) ||
|
|
203
|
+
top.specificObjectives.length < 2 ||
|
|
204
|
+
top.specificObjectives.length > 6) {
|
|
205
|
+
errors.push("ProtocolDraft.specificObjectives must hold 2 to 6 objectives");
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
top.specificObjectives.forEach((entry, index) => {
|
|
209
|
+
const path = `specificObjectives[${index}]`;
|
|
210
|
+
const item = record(entry, path, ["verb", "object", "deliverable"], errors);
|
|
211
|
+
if (!item)
|
|
212
|
+
return;
|
|
213
|
+
const verb = text(item, "verb", path, errors, 40);
|
|
214
|
+
const object = text(item, "object", path, errors, 300);
|
|
215
|
+
const deliverable = text(item, "deliverable", path, errors, 300);
|
|
216
|
+
if (verb && /\s/.test(verb))
|
|
217
|
+
errors.push(`${path}.verb must be a single action verb`);
|
|
218
|
+
if (verb && object && deliverable)
|
|
219
|
+
specificObjectives.push({ verb, object, deliverable });
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
const justificationInput = record(top.justification, "justification", ["relevance", "novelty", "feasibility", "beneficiaries"], errors);
|
|
223
|
+
const justification = justificationInput
|
|
224
|
+
? {
|
|
225
|
+
relevance: text(justificationInput, "relevance", "justification", errors, 1200) ?? "",
|
|
226
|
+
novelty: text(justificationInput, "novelty", "justification", errors, 1200) ?? "",
|
|
227
|
+
feasibility: text(justificationInput, "feasibility", "justification", errors, 1200) ?? "",
|
|
228
|
+
beneficiaries: text(justificationInput, "beneficiaries", "justification", errors, 1200) ?? "",
|
|
229
|
+
}
|
|
230
|
+
: undefined;
|
|
231
|
+
const scope = text(top, "scope", "ProtocolDraft", errors, 1500);
|
|
232
|
+
const limitations = list(top, "limitations", "ProtocolDraft", errors, {
|
|
233
|
+
max: 10,
|
|
234
|
+
item: 400,
|
|
235
|
+
optional: true,
|
|
236
|
+
});
|
|
237
|
+
const hypotheses = list(top, "hypotheses", "ProtocolDraft", errors, {
|
|
238
|
+
max: 6,
|
|
239
|
+
item: 400,
|
|
240
|
+
optional: true,
|
|
241
|
+
});
|
|
242
|
+
if (context.approach === "quantitative" && hypotheses && hypotheses.length === 0) {
|
|
243
|
+
errors.push("ProtocolDraft.hypotheses is required for a quantitative approach");
|
|
244
|
+
}
|
|
245
|
+
const methodInput = record(top.methodology, "methodology", ["design", "population", "instruments", "analysisPlan", "reportingGuideline"], errors);
|
|
246
|
+
let methodology;
|
|
247
|
+
if (methodInput) {
|
|
248
|
+
const design = text(methodInput, "design", "methodology", errors, 600);
|
|
249
|
+
const population = text(methodInput, "population", "methodology", errors, 600);
|
|
250
|
+
const instruments = list(methodInput, "instruments", "methodology", errors, {
|
|
251
|
+
max: 8,
|
|
252
|
+
item: 300,
|
|
253
|
+
optional: true,
|
|
254
|
+
});
|
|
255
|
+
const analysisPlan = text(methodInput, "analysisPlan", "methodology", errors, 1200);
|
|
256
|
+
let reportingGuideline = null;
|
|
257
|
+
if (methodInput.reportingGuideline !== undefined && methodInput.reportingGuideline !== null) {
|
|
258
|
+
reportingGuideline =
|
|
259
|
+
text(methodInput, "reportingGuideline", "methodology", errors, 100) ?? null;
|
|
260
|
+
}
|
|
261
|
+
if (design && population && instruments && analysisPlan) {
|
|
262
|
+
methodology = { design, population, instruments, analysisPlan, reportingGuideline };
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const ethics = {};
|
|
266
|
+
const ethicsInput = record(top.ethics, "ethics", ethicsIds, errors);
|
|
267
|
+
if (ethicsInput) {
|
|
268
|
+
for (const id of ethicsIds) {
|
|
269
|
+
if (typeof ethicsInput[id] !== "boolean")
|
|
270
|
+
errors.push(`ethics.${id} must be true or false`);
|
|
271
|
+
else
|
|
272
|
+
ethics[id] = ethicsInput[id];
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const openQuestions = list(top, "openQuestions", "ProtocolDraft", errors, {
|
|
276
|
+
max: 10,
|
|
277
|
+
item: 400,
|
|
278
|
+
optional: true,
|
|
279
|
+
});
|
|
280
|
+
if (!problem ||
|
|
281
|
+
!researchQuestions ||
|
|
282
|
+
!generalObjective ||
|
|
283
|
+
!justification ||
|
|
284
|
+
!scope ||
|
|
285
|
+
!limitations ||
|
|
286
|
+
!hypotheses ||
|
|
287
|
+
!methodology ||
|
|
288
|
+
!openQuestions) {
|
|
289
|
+
return { errors: errors.length ? errors : ["ProtocolDraft is incomplete"] };
|
|
290
|
+
}
|
|
291
|
+
return finish(errors, {
|
|
292
|
+
problem,
|
|
293
|
+
researchQuestions,
|
|
294
|
+
generalObjective,
|
|
295
|
+
specificObjectives,
|
|
296
|
+
justification,
|
|
297
|
+
scope,
|
|
298
|
+
limitations,
|
|
299
|
+
hypotheses,
|
|
300
|
+
methodology,
|
|
301
|
+
ethics,
|
|
302
|
+
openQuestions,
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
// ---------------------------------------------------------------------------------------------
|
|
306
|
+
// OutlineDraft (structure only; coverage, order and cycles are the OUT checks)
|
|
307
|
+
// ---------------------------------------------------------------------------------------------
|
|
308
|
+
export const evidenceNeeds = [
|
|
309
|
+
"empirical",
|
|
310
|
+
"theoretical",
|
|
311
|
+
"normative",
|
|
312
|
+
"statistical",
|
|
313
|
+
"methodological",
|
|
314
|
+
];
|
|
315
|
+
const nodeKeys = [
|
|
316
|
+
"key",
|
|
317
|
+
"title",
|
|
318
|
+
"requiredKey",
|
|
319
|
+
"purpose",
|
|
320
|
+
"objectives",
|
|
321
|
+
"researchTopics",
|
|
322
|
+
"questionsToAnswer",
|
|
323
|
+
"evidenceNeeds",
|
|
324
|
+
"targetWords",
|
|
325
|
+
"dependsOn",
|
|
326
|
+
"children",
|
|
327
|
+
];
|
|
328
|
+
const keyPattern = /^[a-z][a-z0-9-]{0,40}$/;
|
|
329
|
+
const requiredKeyPattern = /^[a-z][a-z0-9_]{0,40}$/;
|
|
330
|
+
export const maxOutlineNodes = 150;
|
|
331
|
+
export function validateOutlineDraft(value, context) {
|
|
332
|
+
const errors = [];
|
|
333
|
+
const top = record(value, "OutlineDraft", ["sections"], errors);
|
|
334
|
+
if (!top)
|
|
335
|
+
return { errors };
|
|
336
|
+
const seen = new Set();
|
|
337
|
+
const dependencies = [];
|
|
338
|
+
let count = 0;
|
|
339
|
+
const readNode = (entry, path, depth) => {
|
|
340
|
+
count += 1;
|
|
341
|
+
const node = record(entry, path, nodeKeys, errors);
|
|
342
|
+
if (!node)
|
|
343
|
+
return undefined;
|
|
344
|
+
const key = text(node, "key", path, errors, 41);
|
|
345
|
+
if (key) {
|
|
346
|
+
if (!keyPattern.test(key))
|
|
347
|
+
errors.push(`${path}.key must match ${keyPattern}`);
|
|
348
|
+
else if (seen.has(key))
|
|
349
|
+
errors.push(`${path}.key "${key}" is used twice`);
|
|
350
|
+
else
|
|
351
|
+
seen.add(key);
|
|
352
|
+
}
|
|
353
|
+
const title = text(node, "title", path, errors, 200);
|
|
354
|
+
const purpose = text(node, "purpose", path, errors, 300);
|
|
355
|
+
const requiredKey = text(node, "requiredKey", path, errors, 41, { optional: true });
|
|
356
|
+
if (requiredKey && !requiredKeyPattern.test(requiredKey))
|
|
357
|
+
errors.push(`${path}.requiredKey must be snake_case`);
|
|
358
|
+
const objectives = list(node, "objectives", path, errors, { max: 7, item: 10 });
|
|
359
|
+
for (const objective of objectives ?? []) {
|
|
360
|
+
if (!context.objectiveIds.includes(objective)) {
|
|
361
|
+
errors.push(`${path}.objectives lists unknown objective ${objective} (known: ${context.objectiveIds.join(", ")})`);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
const researchTopics = list(node, "researchTopics", path, errors, {
|
|
365
|
+
min: 1,
|
|
366
|
+
max: 8,
|
|
367
|
+
item: 200,
|
|
368
|
+
});
|
|
369
|
+
const questionsToAnswer = list(node, "questionsToAnswer", path, errors, { max: 8, item: 300 });
|
|
370
|
+
const needs = list(node, "evidenceNeeds", path, errors, { max: 5, item: 20 });
|
|
371
|
+
for (const need of needs ?? []) {
|
|
372
|
+
if (!evidenceNeeds.includes(need)) {
|
|
373
|
+
errors.push(`${path}.evidenceNeeds: "${need}" is not one of ${evidenceNeeds.join(", ")}`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
const targetWords = integer(node, "targetWords", path, errors, 1, 100000);
|
|
377
|
+
const dependsOn = list(node, "dependsOn", path, errors, { max: 20, item: 41, optional: true });
|
|
378
|
+
if (dependsOn)
|
|
379
|
+
dependencies.push({ path, keys: dependsOn });
|
|
380
|
+
const children = [];
|
|
381
|
+
if (node.children !== undefined) {
|
|
382
|
+
if (!Array.isArray(node.children) || node.children.length > 99) {
|
|
383
|
+
errors.push(`${path}.children must be a list of at most 99 sections`);
|
|
384
|
+
}
|
|
385
|
+
else if (node.children.length > 0 && depth >= 3) {
|
|
386
|
+
errors.push(`${path}: sections nest at most three levels (SEC-01.01.01)`);
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
node.children.forEach((child, index) => {
|
|
390
|
+
const read = readNode(child, `${path}.children[${index}]`, depth + 1);
|
|
391
|
+
if (read)
|
|
392
|
+
children.push(read);
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
if (!key ||
|
|
397
|
+
!title ||
|
|
398
|
+
!purpose ||
|
|
399
|
+
!objectives ||
|
|
400
|
+
!researchTopics ||
|
|
401
|
+
!questionsToAnswer ||
|
|
402
|
+
!needs ||
|
|
403
|
+
targetWords === undefined ||
|
|
404
|
+
!dependsOn) {
|
|
405
|
+
return undefined;
|
|
406
|
+
}
|
|
407
|
+
return {
|
|
408
|
+
key,
|
|
409
|
+
title,
|
|
410
|
+
...(requiredKey ? { requiredKey } : {}),
|
|
411
|
+
purpose,
|
|
412
|
+
objectives,
|
|
413
|
+
researchTopics,
|
|
414
|
+
questionsToAnswer,
|
|
415
|
+
evidenceNeeds: needs,
|
|
416
|
+
targetWords,
|
|
417
|
+
dependsOn,
|
|
418
|
+
children,
|
|
419
|
+
};
|
|
420
|
+
};
|
|
421
|
+
const sections = [];
|
|
422
|
+
if (!Array.isArray(top.sections) || top.sections.length < 1 || top.sections.length > 99) {
|
|
423
|
+
errors.push("OutlineDraft.sections must hold 1 to 99 sections");
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
top.sections.forEach((entry, index) => {
|
|
427
|
+
const read = readNode(entry, `sections[${index}]`, 1);
|
|
428
|
+
if (read)
|
|
429
|
+
sections.push(read);
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
if (count > maxOutlineNodes)
|
|
433
|
+
errors.push(`The outline has ${count} sections; the limit is ${maxOutlineNodes}`);
|
|
434
|
+
for (const { path, keys } of dependencies) {
|
|
435
|
+
for (const key of keys) {
|
|
436
|
+
if (!seen.has(key))
|
|
437
|
+
errors.push(`${path}.dependsOn names unknown section key "${key}"`);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return finish(errors, { sections });
|
|
441
|
+
}
|
|
442
|
+
export function validateSearchPlan(value) {
|
|
443
|
+
const errors = [];
|
|
444
|
+
const top = record(value, "SearchPlan", ["queries", "yearRangeReason", "webToolsAvailable", "gaps"], errors);
|
|
445
|
+
if (!top)
|
|
446
|
+
return { errors };
|
|
447
|
+
const queries = [];
|
|
448
|
+
if (!Array.isArray(top.queries) || top.queries.length < 1 || top.queries.length > 24) {
|
|
449
|
+
errors.push("SearchPlan.queries must hold 1 to 24 queries");
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
top.queries.forEach((entry, index) => {
|
|
453
|
+
const path = `queries[${index}]`;
|
|
454
|
+
const item = record(entry, path, ["topic", "query", "source", "language", "fromYear", "toYear", "limit"], errors);
|
|
455
|
+
if (!item)
|
|
456
|
+
return;
|
|
457
|
+
const topic = text(item, "topic", path, errors, 200);
|
|
458
|
+
const query = text(item, "query", path, errors, 512);
|
|
459
|
+
const source = oneOf(item, "source", path, errors, [
|
|
460
|
+
"openalex",
|
|
461
|
+
"crossref",
|
|
462
|
+
"arxiv",
|
|
463
|
+
]);
|
|
464
|
+
const language = text(item, "language", path, errors, 3, { optional: true, min: 2 });
|
|
465
|
+
const fromYear = integer(item, "fromYear", path, errors, 1600, 2200, true);
|
|
466
|
+
const toYear = integer(item, "toYear", path, errors, 1600, 2200, true);
|
|
467
|
+
const limit = integer(item, "limit", path, errors, 1, 25, true) ?? 10;
|
|
468
|
+
if (fromYear !== undefined && toYear !== undefined && fromYear > toYear)
|
|
469
|
+
errors.push(`${path}: fromYear is after toYear`);
|
|
470
|
+
if (topic && query && source) {
|
|
471
|
+
queries.push({
|
|
472
|
+
topic,
|
|
473
|
+
query,
|
|
474
|
+
source,
|
|
475
|
+
...(language ? { language: language.toLowerCase() } : {}),
|
|
476
|
+
...(fromYear !== undefined ? { fromYear } : {}),
|
|
477
|
+
...(toYear !== undefined ? { toYear } : {}),
|
|
478
|
+
limit,
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
const reason = text(top, "yearRangeReason", "SearchPlan", errors, 300, { optional: true });
|
|
484
|
+
const gaps = list(top, "gaps", "SearchPlan", errors, { max: 10, item: 400, optional: true });
|
|
485
|
+
if (top.webToolsAvailable !== undefined && typeof top.webToolsAvailable !== "boolean") {
|
|
486
|
+
errors.push("SearchPlan.webToolsAvailable must be true or false");
|
|
487
|
+
}
|
|
488
|
+
return finish(errors, {
|
|
489
|
+
queries,
|
|
490
|
+
...(reason ? { yearRangeReason: reason } : {}),
|
|
491
|
+
webToolsAvailable: top.webToolsAvailable === true,
|
|
492
|
+
gaps: gaps ?? [],
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
export function validateCandidateSet(value) {
|
|
496
|
+
const errors = [];
|
|
497
|
+
const top = record(value, "CandidateSet", ["candidates", "gaps", "webToolsUsed"], errors);
|
|
498
|
+
if (!top)
|
|
499
|
+
return { errors };
|
|
500
|
+
const candidates = [];
|
|
501
|
+
if (!Array.isArray(top.candidates) || top.candidates.length > 40) {
|
|
502
|
+
errors.push("CandidateSet.candidates must be a list of at most 40 candidates (it may be empty)");
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
top.candidates.forEach((entry, index) => {
|
|
506
|
+
const path = `candidates[${index}]`;
|
|
507
|
+
const item = record(entry, path, [
|
|
508
|
+
"identifier",
|
|
509
|
+
"topic",
|
|
510
|
+
"reason",
|
|
511
|
+
"title",
|
|
512
|
+
"firstAuthor",
|
|
513
|
+
"year",
|
|
514
|
+
"type",
|
|
515
|
+
"url",
|
|
516
|
+
"language",
|
|
517
|
+
], errors);
|
|
518
|
+
if (!item)
|
|
519
|
+
return;
|
|
520
|
+
const identifier = text(item, "identifier", path, errors, 2048);
|
|
521
|
+
const topic = text(item, "topic", path, errors, 200);
|
|
522
|
+
const reason = text(item, "reason", path, errors, 300);
|
|
523
|
+
const title = text(item, "title", path, errors, 500);
|
|
524
|
+
const firstAuthor = text(item, "firstAuthor", path, errors, 120, { optional: true });
|
|
525
|
+
const year = integer(item, "year", path, errors, 1000, 2200);
|
|
526
|
+
const type = oneOf(item, "type", path, errors, evidenceTypes, true);
|
|
527
|
+
const url = text(item, "url", path, errors, 500, { optional: true });
|
|
528
|
+
const language = text(item, "language", path, errors, 12, { optional: true });
|
|
529
|
+
if (identifier && topic && reason && title && year !== undefined) {
|
|
530
|
+
candidates.push({
|
|
531
|
+
identifier,
|
|
532
|
+
topic,
|
|
533
|
+
reason,
|
|
534
|
+
title,
|
|
535
|
+
firstAuthor: firstAuthor ?? null,
|
|
536
|
+
year,
|
|
537
|
+
...(type ? { type } : {}),
|
|
538
|
+
...(url ? { url } : {}),
|
|
539
|
+
...(language ? { language } : {}),
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
const gaps = list(top, "gaps", "CandidateSet", errors, { max: 10, item: 400, optional: true });
|
|
545
|
+
if (top.webToolsUsed !== undefined && typeof top.webToolsUsed !== "boolean") {
|
|
546
|
+
errors.push("CandidateSet.webToolsUsed must be true or false");
|
|
547
|
+
}
|
|
548
|
+
return finish(errors, { candidates, gaps: gaps ?? [], webToolsUsed: top.webToolsUsed === true });
|
|
549
|
+
}
|
|
550
|
+
/** `assigned` maps each candidate handle to the status code gave it; every handle must be appraised. */
|
|
551
|
+
export function validateAppraisalSet(value, context) {
|
|
552
|
+
const errors = [];
|
|
553
|
+
const top = record(value, "AppraisalSet", ["appraisals"], errors);
|
|
554
|
+
if (!top)
|
|
555
|
+
return { errors };
|
|
556
|
+
const appraisals = [];
|
|
557
|
+
const seen = new Set();
|
|
558
|
+
if (!Array.isArray(top.appraisals) || top.appraisals.length > 60) {
|
|
559
|
+
errors.push("AppraisalSet.appraisals must be a list of at most 60 entries");
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
top.appraisals.forEach((entry, index) => {
|
|
563
|
+
const path = `appraisals[${index}]`;
|
|
564
|
+
const item = record(entry, path, [
|
|
565
|
+
"handle",
|
|
566
|
+
"relevance",
|
|
567
|
+
"evidenceType",
|
|
568
|
+
"limitations",
|
|
569
|
+
"supports",
|
|
570
|
+
"location",
|
|
571
|
+
"permittedUse",
|
|
572
|
+
"status",
|
|
573
|
+
], errors);
|
|
574
|
+
if (!item)
|
|
575
|
+
return;
|
|
576
|
+
const handle = text(item, "handle", path, errors, 20);
|
|
577
|
+
if (handle) {
|
|
578
|
+
if (!context.assigned.has(handle))
|
|
579
|
+
errors.push(`${path}.handle "${handle}" is not one of: ${[...context.assigned.keys()].join(", ")}`);
|
|
580
|
+
else if (seen.has(handle))
|
|
581
|
+
errors.push(`${path}.handle "${handle}" is appraised twice`);
|
|
582
|
+
else
|
|
583
|
+
seen.add(handle);
|
|
584
|
+
}
|
|
585
|
+
const relevance = oneOf(item, "relevance", path, errors, ["high", "medium", "low"]);
|
|
586
|
+
const evidenceType = text(item, "evidenceType", path, errors, 80);
|
|
587
|
+
const limitations = list(item, "limitations", path, errors, { min: 1, max: 5, item: 300 });
|
|
588
|
+
const supports = list(item, "supports", path, errors, { max: 8, item: 200 });
|
|
589
|
+
const location = text(item, "location", path, errors, 200, { optional: true });
|
|
590
|
+
const uses = list(item, "permittedUse", path, errors, { min: 1, max: 4, item: 30 });
|
|
591
|
+
for (const use of uses ?? []) {
|
|
592
|
+
if (!permittedUses.includes(use))
|
|
593
|
+
errors.push(`${path}.permittedUse: "${use}" is not one of ${permittedUses.join(", ")}`);
|
|
594
|
+
}
|
|
595
|
+
const status = oneOf(item, "status", path, errors, evidenceStatuses, true);
|
|
596
|
+
if (handle && status) {
|
|
597
|
+
const assigned = context.assigned.get(handle);
|
|
598
|
+
if (assigned && evidenceStatuses.indexOf(status) < evidenceStatuses.indexOf(assigned)) {
|
|
599
|
+
errors.push(`${path}.status ${status} would upgrade ${assigned}; an appraisal may only downgrade a status`);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
if (handle && relevance && evidenceType && limitations && supports && uses) {
|
|
603
|
+
appraisals.push({
|
|
604
|
+
handle,
|
|
605
|
+
relevance,
|
|
606
|
+
evidenceType,
|
|
607
|
+
limitations,
|
|
608
|
+
supports,
|
|
609
|
+
...(location ? { location } : {}),
|
|
610
|
+
permittedUse: uses,
|
|
611
|
+
...(status ? { status } : {}),
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
for (const handle of context.assigned.keys()) {
|
|
617
|
+
if (!seen.has(handle))
|
|
618
|
+
errors.push(`AppraisalSet is missing an appraisal for ${handle}`);
|
|
619
|
+
}
|
|
620
|
+
return finish(errors, { appraisals });
|
|
621
|
+
}
|
|
622
|
+
export function validateDossier(value, context) {
|
|
623
|
+
const errors = [];
|
|
624
|
+
const top = record(value, "Dossier", ["synthesis", "claims", "gaps"], errors);
|
|
625
|
+
if (!top)
|
|
626
|
+
return { errors };
|
|
627
|
+
const knownIds = new Set(context.evidenceIds);
|
|
628
|
+
const knownKeys = new Set(context.citeKeys);
|
|
629
|
+
const checkKeys = (path, content) => {
|
|
630
|
+
for (const match of content.matchAll(/@([a-z][a-z0-9]*\d{4}[a-z]?)\b/g)) {
|
|
631
|
+
if (!knownKeys.has(match[1]))
|
|
632
|
+
errors.push(`${path} cites unknown key @${match[1]}`);
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
const evidence = (item, path) => {
|
|
636
|
+
const ids = list(item, "evidence", path, errors, { max: 20, item: 20 });
|
|
637
|
+
for (const id of ids ?? [])
|
|
638
|
+
if (!knownIds.has(id))
|
|
639
|
+
errors.push(`${path}.evidence names unknown evidence id ${id}`);
|
|
640
|
+
return ids ?? [];
|
|
641
|
+
};
|
|
642
|
+
const synthesis = [];
|
|
643
|
+
if (!Array.isArray(top.synthesis) || top.synthesis.length > 12)
|
|
644
|
+
errors.push("Dossier.synthesis must be a list of at most 12 entries");
|
|
645
|
+
else {
|
|
646
|
+
top.synthesis.forEach((entry, index) => {
|
|
647
|
+
const path = `synthesis[${index}]`;
|
|
648
|
+
const item = record(entry, path, ["topic", "summary", "evidence"], errors);
|
|
649
|
+
if (!item)
|
|
650
|
+
return;
|
|
651
|
+
const topic = text(item, "topic", path, errors, 200);
|
|
652
|
+
const summary = text(item, "summary", path, errors, 1500);
|
|
653
|
+
const ids = evidence(item, path);
|
|
654
|
+
if (summary)
|
|
655
|
+
checkKeys(path, summary);
|
|
656
|
+
if (topic && summary)
|
|
657
|
+
synthesis.push({ topic, summary, evidence: ids });
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
const claims = [];
|
|
661
|
+
if (!Array.isArray(top.claims) || top.claims.length > 30)
|
|
662
|
+
errors.push("Dossier.claims must be a list of at most 30 entries");
|
|
663
|
+
else {
|
|
664
|
+
top.claims.forEach((entry, index) => {
|
|
665
|
+
const path = `claims[${index}]`;
|
|
666
|
+
const item = record(entry, path, ["text", "kind", "evidence", "topic"], errors);
|
|
667
|
+
if (!item)
|
|
668
|
+
return;
|
|
669
|
+
const claim = text(item, "text", path, errors, 400);
|
|
670
|
+
const kind = oneOf(item, "kind", path, errors, claimKinds);
|
|
671
|
+
const ids = evidence(item, path);
|
|
672
|
+
const topic = text(item, "topic", path, errors, 200, { optional: true });
|
|
673
|
+
if (claim)
|
|
674
|
+
checkKeys(path, claim);
|
|
675
|
+
if (claim && kind)
|
|
676
|
+
claims.push({ text: claim, kind, evidence: ids, ...(topic ? { topic } : {}) });
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
const gaps = [];
|
|
680
|
+
if (!Array.isArray(top.gaps) || top.gaps.length > 20)
|
|
681
|
+
errors.push("Dossier.gaps must be a list of at most 20 entries");
|
|
682
|
+
else {
|
|
683
|
+
top.gaps.forEach((entry, index) => {
|
|
684
|
+
const path = `gaps[${index}]`;
|
|
685
|
+
const item = record(entry, path, ["topic", "description", "critical"], errors);
|
|
686
|
+
if (!item)
|
|
687
|
+
return;
|
|
688
|
+
const topic = text(item, "topic", path, errors, 200);
|
|
689
|
+
const description = text(item, "description", path, errors, 400);
|
|
690
|
+
if (typeof item.critical !== "boolean")
|
|
691
|
+
errors.push(`${path}.critical must be true or false`);
|
|
692
|
+
if (topic && description && typeof item.critical === "boolean")
|
|
693
|
+
gaps.push({ topic, description, critical: item.critical });
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
const covered = new Set([...synthesis, ...gaps].map((entry) => fold(entry.topic)));
|
|
697
|
+
for (const topic of context.topics) {
|
|
698
|
+
if (!covered.has(fold(topic)))
|
|
699
|
+
errors.push(`Dossier does not cover the research topic "${topic}" in synthesis or gaps; copy each topic exactly`);
|
|
700
|
+
}
|
|
701
|
+
return finish(errors, { synthesis, claims, gaps });
|
|
702
|
+
}
|
|
703
|
+
const figureLabel = /^(fig|tbl)-[a-z0-9-]{1,48}$/;
|
|
704
|
+
const widthText = /^\d{1,3}(?:\.\d+)?%$|^\d{1,3}(?:\.\d+)?(?:cm|mm|in|pt)$/;
|
|
705
|
+
const anchorText = /^[A-Za-z0-9_-]{1,40}$/;
|
|
706
|
+
export function validateFigureDraft(value, path = "FigureDraft") {
|
|
707
|
+
const errors = [];
|
|
708
|
+
const item = record(value, path, ["label", "kind", "spec", "source", "table", "caption", "width", "supports"], errors);
|
|
709
|
+
if (!item)
|
|
710
|
+
return { errors };
|
|
711
|
+
const label = text(item, "label", path, errors, 60);
|
|
712
|
+
if (label !== undefined && !figureLabel.test(label))
|
|
713
|
+
errors.push(`${path}.label must look like fig-adoption-rate or tbl-results`);
|
|
714
|
+
const kind = oneOf(item, "kind", path, errors, ["chart", "diagram", "table"]);
|
|
715
|
+
const caption = text(item, "caption", path, errors, 600);
|
|
716
|
+
if (caption !== undefined && /[\n[\]]/.test(caption))
|
|
717
|
+
errors.push(`${path}.caption must be one line without [ or ] (cite sources as @key)`);
|
|
718
|
+
const supports = text(item, "supports", path, errors, 300, { optional: true }) ?? "";
|
|
719
|
+
const width = text(item, "width", path, errors, 12, { optional: true });
|
|
720
|
+
if (width !== undefined && !widthText.test(width))
|
|
721
|
+
errors.push(`${path}.width must look like 80% or 12cm`);
|
|
722
|
+
let spec;
|
|
723
|
+
let source;
|
|
724
|
+
let table;
|
|
725
|
+
if (kind === "chart") {
|
|
726
|
+
if (!isObject(item.spec) || JSON.stringify(item.spec).length > 200_000)
|
|
727
|
+
errors.push(`${path}.spec must be a Vega-Lite object of at most 200 KB`);
|
|
728
|
+
else
|
|
729
|
+
spec = item.spec;
|
|
730
|
+
if (label && !label.startsWith("fig-"))
|
|
731
|
+
errors.push(`${path}.label of a chart must start with fig-`);
|
|
732
|
+
}
|
|
733
|
+
else if (kind === "diagram") {
|
|
734
|
+
source = text(item, "source", path, errors, 20_000);
|
|
735
|
+
if (label && !label.startsWith("fig-"))
|
|
736
|
+
errors.push(`${path}.label of a diagram must start with fig-`);
|
|
737
|
+
}
|
|
738
|
+
else if (kind === "table") {
|
|
739
|
+
table = text(item, "table", path, errors, 20_000);
|
|
740
|
+
if (label && !label.startsWith("tbl-"))
|
|
741
|
+
errors.push(`${path}.label of a table must start with tbl-`);
|
|
742
|
+
}
|
|
743
|
+
if (errors.length > 0 || !label || !kind || !caption)
|
|
744
|
+
return { errors };
|
|
745
|
+
return {
|
|
746
|
+
value: {
|
|
747
|
+
label,
|
|
748
|
+
kind,
|
|
749
|
+
...(spec ? { spec } : {}),
|
|
750
|
+
...(source ? { source } : {}),
|
|
751
|
+
...(table ? { table } : {}),
|
|
752
|
+
caption,
|
|
753
|
+
...(width ? { width } : {}),
|
|
754
|
+
supports,
|
|
755
|
+
},
|
|
756
|
+
errors: [],
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
export function validateSectionDraft(value, context) {
|
|
760
|
+
const errors = [];
|
|
761
|
+
const top = record(value, "SectionDraft", ["markdown", "keywords", "claims", "figures", "gaps"], errors);
|
|
762
|
+
if (!top)
|
|
763
|
+
return { errors };
|
|
764
|
+
const markdown = text(top, "markdown", "SectionDraft", errors, 80_000);
|
|
765
|
+
const keywords = list(top, "keywords", "SectionDraft", errors, {
|
|
766
|
+
max: 12,
|
|
767
|
+
item: 80,
|
|
768
|
+
optional: true,
|
|
769
|
+
});
|
|
770
|
+
const gaps = list(top, "gaps", "SectionDraft", errors, { max: 20, item: 400, optional: true });
|
|
771
|
+
const evidenceIds = new Set(context.evidenceIds);
|
|
772
|
+
const objectiveIds = new Set(context.objectiveIds);
|
|
773
|
+
const claims = [];
|
|
774
|
+
const anchors = new Set();
|
|
775
|
+
const rawClaims = top.claims ?? [];
|
|
776
|
+
if (!Array.isArray(rawClaims) || rawClaims.length > 60)
|
|
777
|
+
errors.push("SectionDraft.claims must be a list of at most 60 entries");
|
|
778
|
+
else {
|
|
779
|
+
rawClaims.forEach((entry, index) => {
|
|
780
|
+
const path = `claims[${index}]`;
|
|
781
|
+
const item = record(entry, path, ["anchor", "text", "kind", "evidence", "results", "objectives"], errors);
|
|
782
|
+
if (!item)
|
|
783
|
+
return;
|
|
784
|
+
const anchor = text(item, "anchor", path, errors, 40);
|
|
785
|
+
if (anchor !== undefined) {
|
|
786
|
+
if (!anchorText.test(anchor))
|
|
787
|
+
errors.push(`${path}.anchor must use letters, digits, _ and -`);
|
|
788
|
+
else if (anchors.has(anchor))
|
|
789
|
+
errors.push(`${path}.anchor ${anchor} is used twice`);
|
|
790
|
+
else
|
|
791
|
+
anchors.add(anchor);
|
|
792
|
+
}
|
|
793
|
+
const claim = text(item, "text", path, errors, 400);
|
|
794
|
+
const kind = oneOf(item, "kind", path, errors, claimKinds);
|
|
795
|
+
const evidence = list(item, "evidence", path, errors, { max: 20, item: 20, optional: true }) ?? [];
|
|
796
|
+
for (const id of evidence)
|
|
797
|
+
if (!evidenceIds.has(id))
|
|
798
|
+
errors.push(`${path}.evidence names evidence ${id} that is not in the packet`);
|
|
799
|
+
const results = list(item, "results", path, errors, { max: 20, item: 40, optional: true }) ?? [];
|
|
800
|
+
const objectives = list(item, "objectives", path, errors, { max: 12, item: 10, optional: true }) ?? [];
|
|
801
|
+
for (const id of objectives)
|
|
802
|
+
if (!objectiveIds.has(id))
|
|
803
|
+
errors.push(`${path}.objectives names unknown objective ${id}`);
|
|
804
|
+
if (anchor && claim && kind)
|
|
805
|
+
claims.push({ anchor, text: claim, kind, evidence, results, objectives });
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
const figures = [];
|
|
809
|
+
const rawFigures = top.figures ?? [];
|
|
810
|
+
if (!Array.isArray(rawFigures) || rawFigures.length > 8)
|
|
811
|
+
errors.push("SectionDraft.figures must be a list of at most 8 entries");
|
|
812
|
+
else {
|
|
813
|
+
const labels = new Set();
|
|
814
|
+
rawFigures.forEach((entry, index) => {
|
|
815
|
+
const checked = validateFigureDraft(entry, `figures[${index}]`);
|
|
816
|
+
if (checked.value === undefined)
|
|
817
|
+
errors.push(...checked.errors);
|
|
818
|
+
else if (labels.has(checked.value.label))
|
|
819
|
+
errors.push(`figures[${index}].label ${checked.value.label} is used twice`);
|
|
820
|
+
else {
|
|
821
|
+
labels.add(checked.value.label);
|
|
822
|
+
figures.push(checked.value);
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
if (errors.length > 0 || !markdown)
|
|
827
|
+
return { errors };
|
|
828
|
+
return {
|
|
829
|
+
value: { markdown, keywords: keywords ?? [], claims, figures, gaps: gaps ?? [] },
|
|
830
|
+
errors: [],
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
export function validateEditPass(value) {
|
|
834
|
+
const errors = [];
|
|
835
|
+
const top = record(value, "EditPass", ["markdown", "changes", "queries"], errors);
|
|
836
|
+
if (!top)
|
|
837
|
+
return { errors };
|
|
838
|
+
const markdown = text(top, "markdown", "EditPass", errors, 80_000);
|
|
839
|
+
const changes = list(top, "changes", "EditPass", errors, { max: 30, item: 300, optional: true });
|
|
840
|
+
const queries = list(top, "queries", "EditPass", errors, { max: 20, item: 400, optional: true });
|
|
841
|
+
if (errors.length > 0 || !markdown)
|
|
842
|
+
return { errors };
|
|
843
|
+
return { value: { markdown, changes: changes ?? [], queries: queries ?? [] }, errors: [] };
|
|
844
|
+
}
|
|
845
|
+
const squash = (value) => value.replace(/\s+/g, " ").trim();
|
|
846
|
+
/**
|
|
847
|
+
* `texts` maps each reviewable section id to the text the reviewer saw; a finding must quote that
|
|
848
|
+
* text (a finding without a location is not a finding) and its route must follow the category.
|
|
849
|
+
*/
|
|
850
|
+
export function validateReviewReport(value, context) {
|
|
851
|
+
const errors = [];
|
|
852
|
+
const top = record(value, "ReviewReport", ["summary", "findings"], errors);
|
|
853
|
+
if (!top)
|
|
854
|
+
return { errors };
|
|
855
|
+
const summary = text(top, "summary", "ReviewReport", errors, 1500);
|
|
856
|
+
const findings = [];
|
|
857
|
+
if (!Array.isArray(top.findings) || top.findings.length > 60)
|
|
858
|
+
errors.push("ReviewReport.findings must be a list of at most 60 entries");
|
|
859
|
+
else {
|
|
860
|
+
top.findings.forEach((entry, index) => {
|
|
861
|
+
const path = `findings[${index}]`;
|
|
862
|
+
const item = record(entry, path, ["severity", "category", "target", "evidence", "description", "routeTo"], errors);
|
|
863
|
+
if (!item)
|
|
864
|
+
return;
|
|
865
|
+
const severity = oneOf(item, "severity", path, errors, [
|
|
866
|
+
"critical",
|
|
867
|
+
"major",
|
|
868
|
+
"minor",
|
|
869
|
+
]);
|
|
870
|
+
const category = oneOf(item, "category", path, errors, reviewCategories);
|
|
871
|
+
const routeTo = oneOf(item, "routeTo", path, errors, reviewRoutes);
|
|
872
|
+
const target = text(item, "target", path, errors, 20);
|
|
873
|
+
const evidence = text(item, "evidence", path, errors, 400, { min: 8 });
|
|
874
|
+
const description = text(item, "description", path, errors, 800);
|
|
875
|
+
if (category && routeTo && routeOfCategory[category] !== routeTo)
|
|
876
|
+
errors.push(`${path}.routeTo for category ${category} must be ${routeOfCategory[category]}`);
|
|
877
|
+
if (target !== undefined) {
|
|
878
|
+
const source = context.texts.get(target);
|
|
879
|
+
if (source === undefined)
|
|
880
|
+
errors.push(`${path}.target ${target} is not one of the reviewed sections`);
|
|
881
|
+
else if (evidence !== undefined && !squash(source).includes(squash(evidence)))
|
|
882
|
+
errors.push(`${path}.evidence is not a verbatim quote of ${target}`);
|
|
883
|
+
}
|
|
884
|
+
if (severity && category && routeTo && target && evidence && description)
|
|
885
|
+
findings.push({ severity, category, target, evidence, description, routeTo });
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
if (errors.length > 0 || !summary)
|
|
889
|
+
return { errors };
|
|
890
|
+
return { value: { summary, findings }, errors: [] };
|
|
891
|
+
}
|
|
892
|
+
export function validateRevisionAdvice(value) {
|
|
893
|
+
const errors = [];
|
|
894
|
+
const top = record(value, "RevisionAdvice", ["guidance", "protocolChange"], errors);
|
|
895
|
+
if (!top)
|
|
896
|
+
return { errors };
|
|
897
|
+
const guidance = text(top, "guidance", "RevisionAdvice", errors, 2000);
|
|
898
|
+
if (typeof top.protocolChange !== "boolean")
|
|
899
|
+
errors.push("RevisionAdvice.protocolChange must be true or false");
|
|
900
|
+
if (errors.length > 0 || !guidance)
|
|
901
|
+
return { errors };
|
|
902
|
+
return { value: { guidance, protocolChange: top.protocolChange }, errors: [] };
|
|
903
|
+
}
|
|
904
|
+
function profileText(top, path, errors) {
|
|
905
|
+
const raw = top.profile;
|
|
906
|
+
if (raw === undefined || raw === null)
|
|
907
|
+
return undefined;
|
|
908
|
+
if (typeof raw === "string") {
|
|
909
|
+
const clean = stripControl(raw).trim();
|
|
910
|
+
if (!clean || clean.length > 60_000)
|
|
911
|
+
errors.push(`${path}.profile must be 1 to 60000 characters`);
|
|
912
|
+
return clean;
|
|
913
|
+
}
|
|
914
|
+
if (isObject(raw))
|
|
915
|
+
return stringifyYaml(raw, { lineWidth: 0 });
|
|
916
|
+
errors.push(`${path}.profile must be YAML text or an object`);
|
|
917
|
+
return undefined;
|
|
918
|
+
}
|
|
919
|
+
function trace(top, key, path, errors, fields) {
|
|
920
|
+
const raw = top[key] ?? [];
|
|
921
|
+
const out = [];
|
|
922
|
+
if (!Array.isArray(raw) || raw.length > 80) {
|
|
923
|
+
errors.push(`${path}.${key} must be a list of at most 80 entries`);
|
|
924
|
+
return out;
|
|
925
|
+
}
|
|
926
|
+
raw.forEach((entry, index) => {
|
|
927
|
+
const where = `${key}[${index}]`;
|
|
928
|
+
const item = record(entry, where, [...fields], errors);
|
|
929
|
+
if (!item)
|
|
930
|
+
return;
|
|
931
|
+
const first = text(item, fields[0], where, errors, 300);
|
|
932
|
+
const second = text(item, fields[1], where, errors, 600);
|
|
933
|
+
const third = item[fields[2]] === ""
|
|
934
|
+
? ""
|
|
935
|
+
: (text(item, fields[2], where, errors, 600, { optional: true }) ?? "");
|
|
936
|
+
if (first && second)
|
|
937
|
+
out.push({ rule: first, guide: second, effect: third });
|
|
938
|
+
});
|
|
939
|
+
return out;
|
|
940
|
+
}
|
|
941
|
+
export function validateStyleDraft(value) {
|
|
942
|
+
const errors = [];
|
|
943
|
+
const top = record(value, "StyleDraft", ["csl", "profile", "questions", "ruleTrace"], errors);
|
|
944
|
+
if (!top)
|
|
945
|
+
return { errors };
|
|
946
|
+
const csl = text(top, "csl", "StyleDraft", errors, 400_000);
|
|
947
|
+
const profile = profileText(top, "StyleDraft", errors);
|
|
948
|
+
const questions = list(top, "questions", "StyleDraft", errors, {
|
|
949
|
+
max: 8,
|
|
950
|
+
item: 400,
|
|
951
|
+
optional: true,
|
|
952
|
+
});
|
|
953
|
+
const ruleTrace = trace(top, "ruleTrace", "StyleDraft", errors, ["rule", "guide", "effect"]);
|
|
954
|
+
if (errors.length > 0 || !csl)
|
|
955
|
+
return { errors };
|
|
956
|
+
return {
|
|
957
|
+
value: { csl, ...(profile ? { profile } : {}), questions: questions ?? [], ruleTrace },
|
|
958
|
+
errors: [],
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
export const normsScopes = [
|
|
962
|
+
"institution",
|
|
963
|
+
"faculty",
|
|
964
|
+
"program",
|
|
965
|
+
"writing",
|
|
966
|
+
"international",
|
|
967
|
+
];
|
|
968
|
+
export function validateNormsDraft(value) {
|
|
969
|
+
const errors = [];
|
|
970
|
+
const top = record(value, "NormsDraft", ["pack", "rules", "profile", "questions", "sourceTrace"], errors);
|
|
971
|
+
if (!top)
|
|
972
|
+
return { errors };
|
|
973
|
+
const packSource = record(top.pack, "NormsDraft.pack", ["scope", "id", "description", "appliesWhen"], errors);
|
|
974
|
+
let pack;
|
|
975
|
+
if (packSource) {
|
|
976
|
+
const scope = oneOf(packSource, "scope", "NormsDraft.pack", errors, normsScopes);
|
|
977
|
+
const id = text(packSource, "id", "NormsDraft.pack", errors, 60);
|
|
978
|
+
const description = text(packSource, "description", "NormsDraft.pack", errors, 300);
|
|
979
|
+
if (packSource.appliesWhen !== undefined && !isObject(packSource.appliesWhen))
|
|
980
|
+
errors.push("NormsDraft.pack.appliesWhen must be a mapping");
|
|
981
|
+
if (scope && id && description)
|
|
982
|
+
pack = {
|
|
983
|
+
scope,
|
|
984
|
+
id,
|
|
985
|
+
description,
|
|
986
|
+
appliesWhen: isObject(packSource.appliesWhen) ? packSource.appliesWhen : {},
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
const rules = [];
|
|
990
|
+
if (!Array.isArray(top.rules) || top.rules.length < 1 || top.rules.length > 80)
|
|
991
|
+
errors.push("NormsDraft.rules must hold 1 to 80 rules");
|
|
992
|
+
else {
|
|
993
|
+
top.rules.forEach((entry, index) => {
|
|
994
|
+
if (!isObject(entry))
|
|
995
|
+
errors.push(`rules[${index}] must be a mapping`);
|
|
996
|
+
else
|
|
997
|
+
rules.push(entry);
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
const profile = profileText(top, "NormsDraft", errors);
|
|
1001
|
+
const questions = list(top, "questions", "NormsDraft", errors, {
|
|
1002
|
+
max: 8,
|
|
1003
|
+
item: 400,
|
|
1004
|
+
optional: true,
|
|
1005
|
+
});
|
|
1006
|
+
const sourceTrace = trace(top, "sourceTrace", "NormsDraft", errors, ["rule", "guide", "effect"]);
|
|
1007
|
+
if (errors.length > 0 || !pack)
|
|
1008
|
+
return { errors };
|
|
1009
|
+
return {
|
|
1010
|
+
value: {
|
|
1011
|
+
pack,
|
|
1012
|
+
rules,
|
|
1013
|
+
...(profile ? { profile } : {}),
|
|
1014
|
+
questions: questions ?? [],
|
|
1015
|
+
sourceTrace,
|
|
1016
|
+
},
|
|
1017
|
+
errors: [],
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
//# sourceMappingURL=schemas.js.map
|