@transitrix/diagrams 1.0.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 +47 -0
- package/dist/__tests__/edge-path.test.d.ts +1 -0
- package/dist/__tests__/edge-path.test.js +39 -0
- package/dist/__tests__/scope.test.d.ts +1 -0
- package/dist/__tests__/scope.test.js +23 -0
- package/dist/__tests__/yaml-normalize.test.d.ts +1 -0
- package/dist/__tests__/yaml-normalize.test.js +148 -0
- package/dist/activities/__tests__/cpm.test.d.ts +1 -0
- package/dist/activities/__tests__/cpm.test.js +146 -0
- package/dist/activities/__tests__/gantt.test.d.ts +1 -0
- package/dist/activities/__tests__/gantt.test.js +224 -0
- package/dist/activities/__tests__/layout.test.d.ts +1 -0
- package/dist/activities/__tests__/layout.test.js +98 -0
- package/dist/activities/__tests__/validate.test.d.ts +1 -0
- package/dist/activities/__tests__/validate.test.js +363 -0
- package/dist/activities/cpm.d.ts +2 -0
- package/dist/activities/cpm.js +86 -0
- package/dist/activities/gantt.d.ts +6 -0
- package/dist/activities/gantt.js +250 -0
- package/dist/activities/index.d.ts +5 -0
- package/dist/activities/index.js +4 -0
- package/dist/activities/layout.d.ts +2 -0
- package/dist/activities/layout.js +103 -0
- package/dist/activities/types.d.ts +125 -0
- package/dist/activities/types.js +1 -0
- package/dist/activities/validate.d.ts +2 -0
- package/dist/activities/validate.js +326 -0
- package/dist/activity-card/__tests__/example.test.d.ts +1 -0
- package/dist/activity-card/__tests__/example.test.js +56 -0
- package/dist/activity-card/__tests__/layout.test.d.ts +1 -0
- package/dist/activity-card/__tests__/layout.test.js +57 -0
- package/dist/activity-card/__tests__/resolver.test.d.ts +1 -0
- package/dist/activity-card/__tests__/resolver.test.js +146 -0
- package/dist/activity-card/__tests__/validate.test.d.ts +1 -0
- package/dist/activity-card/__tests__/validate.test.js +116 -0
- package/dist/activity-card/index.d.ts +5 -0
- package/dist/activity-card/index.js +3 -0
- package/dist/activity-card/layout.d.ts +7 -0
- package/dist/activity-card/layout.js +167 -0
- package/dist/activity-card/resolver.d.ts +7 -0
- package/dist/activity-card/resolver.js +310 -0
- package/dist/activity-card/types.d.ts +175 -0
- package/dist/activity-card/types.js +20 -0
- package/dist/activity-card/validate.d.ts +3 -0
- package/dist/activity-card/validate.js +145 -0
- package/dist/actor/__tests__/validate.test.d.ts +1 -0
- package/dist/actor/__tests__/validate.test.js +57 -0
- package/dist/actor/index.d.ts +3 -0
- package/dist/actor/index.js +2 -0
- package/dist/actor/types.d.ts +20 -0
- package/dist/actor/types.js +4 -0
- package/dist/actor/validate.d.ts +2 -0
- package/dist/actor/validate.js +34 -0
- package/dist/applications/__tests__/validate.test.d.ts +1 -0
- package/dist/applications/__tests__/validate.test.js +223 -0
- package/dist/applications/index.d.ts +2 -0
- package/dist/applications/index.js +1 -0
- package/dist/applications/types.d.ts +39 -0
- package/dist/applications/types.js +1 -0
- package/dist/applications/validate.d.ts +3 -0
- package/dist/applications/validate.js +109 -0
- package/dist/assertion/__tests__/example.test.d.ts +1 -0
- package/dist/assertion/__tests__/example.test.js +32 -0
- package/dist/assertion/__tests__/validate.test.d.ts +1 -0
- package/dist/assertion/__tests__/validate.test.js +130 -0
- package/dist/assertion/index.d.ts +4 -0
- package/dist/assertion/index.js +2 -0
- package/dist/assertion/types.d.ts +43 -0
- package/dist/assertion/types.js +7 -0
- package/dist/assertion/validate.d.ts +9 -0
- package/dist/assertion/validate.js +137 -0
- package/dist/blocks/__tests__/layout.test.d.ts +1 -0
- package/dist/blocks/__tests__/layout.test.js +153 -0
- package/dist/blocks/__tests__/validate.test.d.ts +1 -0
- package/dist/blocks/__tests__/validate.test.js +235 -0
- package/dist/blocks/index.d.ts +4 -0
- package/dist/blocks/index.js +2 -0
- package/dist/blocks/layout.d.ts +4 -0
- package/dist/blocks/layout.js +145 -0
- package/dist/blocks/types.d.ts +56 -0
- package/dist/blocks/types.js +1 -0
- package/dist/blocks/validate.d.ts +9 -0
- package/dist/blocks/validate.js +186 -0
- package/dist/capability-map/__tests__/validate.test.d.ts +1 -0
- package/dist/capability-map/__tests__/validate.test.js +212 -0
- package/dist/capability-map/index.d.ts +2 -0
- package/dist/capability-map/index.js +1 -0
- package/dist/capability-map/types.d.ts +26 -0
- package/dist/capability-map/types.js +1 -0
- package/dist/capability-map/validate.d.ts +3 -0
- package/dist/capability-map/validate.js +103 -0
- package/dist/change/__tests__/validate.test.d.ts +1 -0
- package/dist/change/__tests__/validate.test.js +73 -0
- package/dist/change/index.d.ts +2 -0
- package/dist/change/index.js +1 -0
- package/dist/change/types.d.ts +17 -0
- package/dist/change/types.js +3 -0
- package/dist/change/validate.d.ts +2 -0
- package/dist/change/validate.js +57 -0
- package/dist/compliance/__tests__/classify.test.d.ts +1 -0
- package/dist/compliance/__tests__/classify.test.js +24 -0
- package/dist/compliance/__tests__/confidence.test.d.ts +1 -0
- package/dist/compliance/__tests__/confidence.test.js +78 -0
- package/dist/compliance/__tests__/coverage-metric.test.d.ts +1 -0
- package/dist/compliance/__tests__/coverage-metric.test.js +164 -0
- package/dist/compliance/__tests__/demo-scenario.test.d.ts +1 -0
- package/dist/compliance/__tests__/demo-scenario.test.js +226 -0
- package/dist/compliance/__tests__/example.test.d.ts +1 -0
- package/dist/compliance/__tests__/example.test.js +51 -0
- package/dist/compliance/__tests__/gap-report.test.d.ts +1 -0
- package/dist/compliance/__tests__/gap-report.test.js +113 -0
- package/dist/compliance/__tests__/html.test.d.ts +1 -0
- package/dist/compliance/__tests__/html.test.js +122 -0
- package/dist/compliance/__tests__/impact.test.d.ts +1 -0
- package/dist/compliance/__tests__/impact.test.js +410 -0
- package/dist/compliance/__tests__/markdown.test.d.ts +1 -0
- package/dist/compliance/__tests__/markdown.test.js +85 -0
- package/dist/compliance/__tests__/views.test.d.ts +1 -0
- package/dist/compliance/__tests__/views.test.js +59 -0
- package/dist/compliance/classify.d.ts +27 -0
- package/dist/compliance/classify.js +60 -0
- package/dist/compliance/confidence.d.ts +27 -0
- package/dist/compliance/confidence.js +43 -0
- package/dist/compliance/coverage-metric.d.ts +74 -0
- package/dist/compliance/coverage-metric.js +181 -0
- package/dist/compliance/gap-report.d.ts +20 -0
- package/dist/compliance/gap-report.js +64 -0
- package/dist/compliance/html.d.ts +22 -0
- package/dist/compliance/html.js +256 -0
- package/dist/compliance/impact.d.ts +251 -0
- package/dist/compliance/impact.js +464 -0
- package/dist/compliance/index.d.ts +16 -0
- package/dist/compliance/index.js +9 -0
- package/dist/compliance/markdown.d.ts +18 -0
- package/dist/compliance/markdown.js +129 -0
- package/dist/compliance/reverse-index.d.ts +8 -0
- package/dist/compliance/reverse-index.js +30 -0
- package/dist/compliance/types.d.ts +98 -0
- package/dist/compliance/types.js +5 -0
- package/dist/compliance/views.d.ts +14 -0
- package/dist/compliance/views.js +35 -0
- package/dist/compliance-matrix/__tests__/build.test.d.ts +1 -0
- package/dist/compliance-matrix/__tests__/build.test.js +183 -0
- package/dist/compliance-matrix/__tests__/example.test.d.ts +1 -0
- package/dist/compliance-matrix/__tests__/example.test.js +49 -0
- package/dist/compliance-matrix/build.d.ts +18 -0
- package/dist/compliance-matrix/build.js +0 -0
- package/dist/compliance-matrix/index.d.ts +2 -0
- package/dist/compliance-matrix/index.js +1 -0
- package/dist/compliance-matrix/types.d.ts +86 -0
- package/dist/compliance-matrix/types.js +8 -0
- package/dist/confidence/__tests__/score.test.d.ts +1 -0
- package/dist/confidence/__tests__/score.test.js +188 -0
- package/dist/confidence/index.d.ts +2 -0
- package/dist/confidence/index.js +1 -0
- package/dist/confidence/score.d.ts +62 -0
- package/dist/confidence/score.js +215 -0
- package/dist/confidence/types.d.ts +79 -0
- package/dist/confidence/types.js +6 -0
- package/dist/edge-path.d.ts +18 -0
- package/dist/edge-path.js +35 -0
- package/dist/element-envelope.d.ts +15 -0
- package/dist/element-envelope.js +55 -0
- package/dist/factor/__tests__/validate.test.d.ts +1 -0
- package/dist/factor/__tests__/validate.test.js +83 -0
- package/dist/factor/index.d.ts +3 -0
- package/dist/factor/index.js +2 -0
- package/dist/factor/types.d.ts +23 -0
- package/dist/factor/types.js +6 -0
- package/dist/factor/validate.d.ts +2 -0
- package/dist/factor/validate.js +69 -0
- package/dist/fgca/__tests__/chain-table.test.d.ts +1 -0
- package/dist/fgca/__tests__/chain-table.test.js +158 -0
- package/dist/fgca/__tests__/layout.test.d.ts +1 -0
- package/dist/fgca/__tests__/layout.test.js +140 -0
- package/dist/fgca/__tests__/parse-canonical.test.d.ts +1 -0
- package/dist/fgca/__tests__/parse-canonical.test.js +224 -0
- package/dist/fgca/__tests__/preview-layout.test.d.ts +1 -0
- package/dist/fgca/__tests__/preview-layout.test.js +106 -0
- package/dist/fgca/__tests__/validate.test.d.ts +1 -0
- package/dist/fgca/__tests__/validate.test.js +125 -0
- package/dist/fgca/chain-table.d.ts +36 -0
- package/dist/fgca/chain-table.js +177 -0
- package/dist/fgca/index.d.ts +16 -0
- package/dist/fgca/index.js +11 -0
- package/dist/fgca/layout.d.ts +27 -0
- package/dist/fgca/layout.js +156 -0
- package/dist/fgca/nodeTypes.d.ts +6 -0
- package/dist/fgca/nodeTypes.js +10 -0
- package/dist/fgca/nodes/FGCAActivityNode.d.ts +13 -0
- package/dist/fgca/nodes/FGCAActivityNode.js +35 -0
- package/dist/fgca/nodes/FGCAChangeNode.d.ts +12 -0
- package/dist/fgca/nodes/FGCAChangeNode.js +35 -0
- package/dist/fgca/nodes/FGCAFactorNode.d.ts +12 -0
- package/dist/fgca/nodes/FGCAFactorNode.js +35 -0
- package/dist/fgca/nodes/FGCAGoalNode.d.ts +14 -0
- package/dist/fgca/nodes/FGCAGoalNode.js +40 -0
- package/dist/fgca/parse-canonical.d.ts +58 -0
- package/dist/fgca/parse-canonical.js +339 -0
- package/dist/fgca/preview-layout.d.ts +87 -0
- package/dist/fgca/preview-layout.js +122 -0
- package/dist/fgca/types.d.ts +39 -0
- package/dist/fgca/types.js +7 -0
- package/dist/fgca/validate.d.ts +14 -0
- package/dist/fgca/validate.js +108 -0
- package/dist/geometry.d.ts +7 -0
- package/dist/geometry.js +2 -0
- package/dist/goals/__tests__/layout.test.d.ts +1 -0
- package/dist/goals/__tests__/layout.test.js +153 -0
- package/dist/goals/__tests__/mutations.test.d.ts +1 -0
- package/dist/goals/__tests__/mutations.test.js +95 -0
- package/dist/goals/__tests__/parse-canonical.test.d.ts +1 -0
- package/dist/goals/__tests__/parse-canonical.test.js +129 -0
- package/dist/goals/__tests__/validate.test.d.ts +1 -0
- package/dist/goals/__tests__/validate.test.js +183 -0
- package/dist/goals/index.d.ts +6 -0
- package/dist/goals/index.js +4 -0
- package/dist/goals/layout.d.ts +11 -0
- package/dist/goals/layout.js +147 -0
- package/dist/goals/mutations.d.ts +6 -0
- package/dist/goals/mutations.js +116 -0
- package/dist/goals/parse-canonical.d.ts +29 -0
- package/dist/goals/parse-canonical.js +257 -0
- package/dist/goals/types.d.ts +71 -0
- package/dist/goals/types.js +1 -0
- package/dist/goals/validate.d.ts +2 -0
- package/dist/goals/validate.js +122 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +23 -0
- package/dist/issues/__tests__/layout.test.d.ts +1 -0
- package/dist/issues/__tests__/layout.test.js +62 -0
- package/dist/issues/__tests__/validate.test.d.ts +1 -0
- package/dist/issues/__tests__/validate.test.js +82 -0
- package/dist/issues/index.d.ts +4 -0
- package/dist/issues/index.js +2 -0
- package/dist/issues/layout.d.ts +10 -0
- package/dist/issues/layout.js +82 -0
- package/dist/issues/types.d.ts +61 -0
- package/dist/issues/types.js +6 -0
- package/dist/issues/validate.d.ts +3 -0
- package/dist/issues/validate.js +143 -0
- package/dist/process-blueprint/__tests__/compliance-lane.test.d.ts +1 -0
- package/dist/process-blueprint/__tests__/compliance-lane.test.js +272 -0
- package/dist/process-blueprint/__tests__/layout.test.d.ts +1 -0
- package/dist/process-blueprint/__tests__/layout.test.js +159 -0
- package/dist/process-blueprint/__tests__/validate.test.d.ts +1 -0
- package/dist/process-blueprint/__tests__/validate.test.js +280 -0
- package/dist/process-blueprint/index.d.ts +4 -0
- package/dist/process-blueprint/index.js +2 -0
- package/dist/process-blueprint/layout.d.ts +2 -0
- package/dist/process-blueprint/layout.js +416 -0
- package/dist/process-blueprint/types.d.ts +220 -0
- package/dist/process-blueprint/types.js +1 -0
- package/dist/process-blueprint/validate.d.ts +3 -0
- package/dist/process-blueprint/validate.js +165 -0
- package/dist/process-map/__tests__/validate.test.d.ts +1 -0
- package/dist/process-map/__tests__/validate.test.js +199 -0
- package/dist/process-map/index.d.ts +2 -0
- package/dist/process-map/index.js +1 -0
- package/dist/process-map/types.d.ts +32 -0
- package/dist/process-map/types.js +1 -0
- package/dist/process-map/validate.d.ts +3 -0
- package/dist/process-map/validate.js +115 -0
- package/dist/products/__tests__/validate.test.d.ts +1 -0
- package/dist/products/__tests__/validate.test.js +210 -0
- package/dist/products/index.d.ts +2 -0
- package/dist/products/index.js +1 -0
- package/dist/products/types.d.ts +28 -0
- package/dist/products/types.js +1 -0
- package/dist/products/validate.d.ts +3 -0
- package/dist/products/validate.js +93 -0
- package/dist/repo-validate/__tests__/validate-repo.test.d.ts +1 -0
- package/dist/repo-validate/__tests__/validate-repo.test.js +132 -0
- package/dist/repo-validate/index.d.ts +2 -0
- package/dist/repo-validate/index.js +1 -0
- package/dist/repo-validate/types.d.ts +34 -0
- package/dist/repo-validate/types.js +14 -0
- package/dist/repo-validate/validate-repo.d.ts +8 -0
- package/dist/repo-validate/validate-repo.js +189 -0
- package/dist/requirement/__tests__/example.test.d.ts +1 -0
- package/dist/requirement/__tests__/example.test.js +21 -0
- package/dist/requirement/__tests__/validate.test.d.ts +1 -0
- package/dist/requirement/__tests__/validate.test.js +80 -0
- package/dist/requirement/index.d.ts +4 -0
- package/dist/requirement/index.js +2 -0
- package/dist/requirement/types.d.ts +25 -0
- package/dist/requirement/types.js +4 -0
- package/dist/requirement/validate.d.ts +7 -0
- package/dist/requirement/validate.js +81 -0
- package/dist/scenarios/__tests__/validate.test.d.ts +1 -0
- package/dist/scenarios/__tests__/validate.test.js +183 -0
- package/dist/scenarios/index.d.ts +2 -0
- package/dist/scenarios/index.js +1 -0
- package/dist/scenarios/types.d.ts +45 -0
- package/dist/scenarios/types.js +1 -0
- package/dist/scenarios/validate.d.ts +3 -0
- package/dist/scenarios/validate.js +110 -0
- package/dist/schema-version.d.ts +16 -0
- package/dist/schema-version.js +16 -0
- package/dist/scope.d.ts +23 -0
- package/dist/scope.js +38 -0
- package/dist/stakeholder/__tests__/validate.test.d.ts +1 -0
- package/dist/stakeholder/__tests__/validate.test.js +86 -0
- package/dist/stakeholder/index.d.ts +3 -0
- package/dist/stakeholder/index.js +2 -0
- package/dist/stakeholder/types.d.ts +24 -0
- package/dist/stakeholder/types.js +5 -0
- package/dist/stakeholder/validate.d.ts +2 -0
- package/dist/stakeholder/validate.js +58 -0
- package/dist/target-state/__tests__/validate.test.d.ts +1 -0
- package/dist/target-state/__tests__/validate.test.js +62 -0
- package/dist/target-state/index.d.ts +2 -0
- package/dist/target-state/index.js +1 -0
- package/dist/target-state/types.d.ts +18 -0
- package/dist/target-state/types.js +7 -0
- package/dist/target-state/validate.d.ts +2 -0
- package/dist/target-state/validate.js +61 -0
- package/dist/theme/index.d.ts +4 -0
- package/dist/theme/index.js +2 -0
- package/dist/theme/themes.d.ts +16 -0
- package/dist/theme/themes.js +213 -0
- package/dist/theme/tokens.d.ts +158 -0
- package/dist/theme/tokens.js +105 -0
- package/dist/typed-id.d.ts +20 -0
- package/dist/typed-id.js +38 -0
- package/dist/validation-types.d.ts +34 -0
- package/dist/validation-types.js +1 -0
- package/dist/webview/__tests__/entry-notations.test.d.ts +1 -0
- package/dist/webview/__tests__/entry-notations.test.js +62 -0
- package/dist/webview/__tests__/entry.test.d.ts +1 -0
- package/dist/webview/__tests__/entry.test.js +56 -0
- package/dist/webview/__tests__/render-goals.test.d.ts +1 -0
- package/dist/webview/__tests__/render-goals.test.js +65 -0
- package/dist/webview/entry.d.ts +38 -0
- package/dist/webview/entry.js +182 -0
- package/dist/webview/render-activities.d.ts +24 -0
- package/dist/webview/render-activities.js +127 -0
- package/dist/webview/render-activity-card.d.ts +5 -0
- package/dist/webview/render-activity-card.js +168 -0
- package/dist/webview/render-applications.d.ts +11 -0
- package/dist/webview/render-applications.js +105 -0
- package/dist/webview/render-blocks.d.ts +5 -0
- package/dist/webview/render-blocks.js +71 -0
- package/dist/webview/render-capability-map.d.ts +7 -0
- package/dist/webview/render-capability-map.js +88 -0
- package/dist/webview/render-fgca.d.ts +8 -0
- package/dist/webview/render-fgca.js +94 -0
- package/dist/webview/render-goals.d.ts +6 -0
- package/dist/webview/render-goals.js +92 -0
- package/dist/webview/render-process-blueprint.d.ts +5 -0
- package/dist/webview/render-process-blueprint.js +135 -0
- package/dist/webview/render-process-map.d.ts +7 -0
- package/dist/webview/render-process-map.js +82 -0
- package/dist/webview/render-products.d.ts +7 -0
- package/dist/webview/render-products.js +81 -0
- package/dist/webview/render-scenarios.d.ts +7 -0
- package/dist/webview/render-scenarios.js +75 -0
- package/dist/webview/render-util.d.ts +16 -0
- package/dist/webview/render-util.js +31 -0
- package/dist/webview/transitrix-render.css +323 -0
- package/dist/webview/transitrix-render.js +6452 -0
- package/dist/webview/transitrix-render.js.map +7 -0
- package/dist/yaml-normalize.d.ts +20 -0
- package/dist/yaml-normalize.js +57 -0
- package/package.json +40 -0
- package/src/__tests__/edge-path.test.ts +46 -0
- package/src/__tests__/scope.test.ts +27 -0
- package/src/__tests__/yaml-normalize.test.ts +158 -0
- package/src/activities/__tests__/cpm.test.ts +164 -0
- package/src/activities/__tests__/gantt.test.ts +245 -0
- package/src/activities/__tests__/layout.test.ts +112 -0
- package/src/activities/__tests__/validate.test.ts +409 -0
- package/src/activities/cpm.ts +95 -0
- package/src/activities/gantt.ts +276 -0
- package/src/activities/index.ts +28 -0
- package/src/activities/layout.ts +112 -0
- package/src/activities/types.ts +148 -0
- package/src/activities/validate.ts +357 -0
- package/src/activity-card/__tests__/example.test.ts +66 -0
- package/src/activity-card/__tests__/layout.test.ts +63 -0
- package/src/activity-card/__tests__/resolver.test.ts +161 -0
- package/src/activity-card/__tests__/validate.test.ts +131 -0
- package/src/activity-card/index.ts +32 -0
- package/src/activity-card/layout.ts +194 -0
- package/src/activity-card/resolver.ts +337 -0
- package/src/activity-card/types.ts +237 -0
- package/src/activity-card/validate.ts +158 -0
- package/src/actor/__tests__/validate.test.ts +67 -0
- package/src/actor/index.ts +3 -0
- package/src/actor/types.ts +29 -0
- package/src/actor/validate.ts +38 -0
- package/src/applications/__tests__/validate.test.ts +256 -0
- package/src/applications/index.ts +11 -0
- package/src/applications/types.ts +43 -0
- package/src/applications/validate.ts +131 -0
- package/src/assertion/__tests__/example.test.ts +37 -0
- package/src/assertion/__tests__/validate.test.ts +142 -0
- package/src/assertion/index.ts +12 -0
- package/src/assertion/types.ts +58 -0
- package/src/assertion/validate.ts +145 -0
- package/src/blocks/__tests__/layout.test.ts +169 -0
- package/src/blocks/__tests__/validate.test.ts +260 -0
- package/src/blocks/index.ts +17 -0
- package/src/blocks/layout.ts +186 -0
- package/src/blocks/types.ts +62 -0
- package/src/blocks/validate.ts +206 -0
- package/src/capability-map/__tests__/validate.test.ts +238 -0
- package/src/capability-map/index.ts +8 -0
- package/src/capability-map/types.ts +29 -0
- package/src/capability-map/validate.ts +127 -0
- package/src/change/__tests__/validate.test.ts +88 -0
- package/src/change/index.ts +2 -0
- package/src/change/types.ts +23 -0
- package/src/change/validate.ts +61 -0
- package/src/compliance/__tests__/classify.test.ts +27 -0
- package/src/compliance/__tests__/confidence.test.ts +98 -0
- package/src/compliance/__tests__/coverage-metric.test.ts +188 -0
- package/src/compliance/__tests__/demo-scenario.test.ts +273 -0
- package/src/compliance/__tests__/example.test.ts +57 -0
- package/src/compliance/__tests__/gap-report.test.ts +129 -0
- package/src/compliance/__tests__/html.test.ts +142 -0
- package/src/compliance/__tests__/impact.test.ts +482 -0
- package/src/compliance/__tests__/markdown.test.ts +95 -0
- package/src/compliance/__tests__/views.test.ts +64 -0
- package/src/compliance/classify.ts +84 -0
- package/src/compliance/confidence.ts +69 -0
- package/src/compliance/coverage-metric.ts +263 -0
- package/src/compliance/gap-report.ts +94 -0
- package/src/compliance/html.ts +295 -0
- package/src/compliance/impact.ts +677 -0
- package/src/compliance/index.ts +56 -0
- package/src/compliance/markdown.ts +135 -0
- package/src/compliance/reverse-index.ts +33 -0
- package/src/compliance/types.ts +120 -0
- package/src/compliance/views.ts +46 -0
- package/src/compliance-matrix/__tests__/build.test.ts +200 -0
- package/src/compliance-matrix/__tests__/example.test.ts +57 -0
- package/src/compliance-matrix/build.ts +0 -0
- package/src/compliance-matrix/index.ts +11 -0
- package/src/compliance-matrix/types.ts +102 -0
- package/src/confidence/__tests__/score.test.ts +254 -0
- package/src/confidence/index.ts +21 -0
- package/src/confidence/score.ts +243 -0
- package/src/confidence/types.ts +96 -0
- package/src/edge-path.ts +44 -0
- package/src/element-envelope.ts +76 -0
- package/src/factor/__tests__/validate.test.ts +98 -0
- package/src/factor/index.ts +8 -0
- package/src/factor/types.ts +41 -0
- package/src/factor/validate.ts +74 -0
- package/src/fgca/__tests__/chain-table.test.ts +172 -0
- package/src/fgca/__tests__/layout.test.ts +179 -0
- package/src/fgca/__tests__/parse-canonical.test.ts +248 -0
- package/src/fgca/__tests__/preview-layout.test.ts +124 -0
- package/src/fgca/__tests__/validate.test.ts +142 -0
- package/src/fgca/chain-table.ts +225 -0
- package/src/fgca/index.ts +45 -0
- package/src/fgca/layout.ts +204 -0
- package/src/fgca/nodeTypes.ts +11 -0
- package/src/fgca/nodes/FGCAActivityNode.tsx +65 -0
- package/src/fgca/nodes/FGCAChangeNode.tsx +64 -0
- package/src/fgca/nodes/FGCAFactorNode.tsx +64 -0
- package/src/fgca/nodes/FGCAGoalNode.tsx +72 -0
- package/src/fgca/parse-canonical.ts +400 -0
- package/src/fgca/preview-layout.ts +217 -0
- package/src/fgca/types.ts +50 -0
- package/src/fgca/validate.ts +126 -0
- package/src/geometry.ts +9 -0
- package/src/goals/__tests__/layout.test.ts +175 -0
- package/src/goals/__tests__/mutations.test.ts +111 -0
- package/src/goals/__tests__/parse-canonical.test.ts +145 -0
- package/src/goals/__tests__/validate.test.ts +202 -0
- package/src/goals/index.ts +21 -0
- package/src/goals/layout.ts +172 -0
- package/src/goals/mutations.ts +123 -0
- package/src/goals/parse-canonical.ts +286 -0
- package/src/goals/types.ts +85 -0
- package/src/goals/validate.ts +136 -0
- package/src/index.ts +23 -0
- package/src/process-blueprint/__tests__/compliance-lane.test.ts +320 -0
- package/src/process-blueprint/__tests__/layout.test.ts +195 -0
- package/src/process-blueprint/__tests__/validate.test.ts +310 -0
- package/src/process-blueprint/index.ts +31 -0
- package/src/process-blueprint/layout.ts +531 -0
- package/src/process-blueprint/types.ts +242 -0
- package/src/process-blueprint/validate.ts +181 -0
- package/src/process-map/__tests__/validate.test.ts +223 -0
- package/src/process-map/index.ts +10 -0
- package/src/process-map/types.ts +36 -0
- package/src/process-map/validate.ts +135 -0
- package/src/products/__tests__/validate.test.ts +239 -0
- package/src/products/index.ts +9 -0
- package/src/products/types.ts +31 -0
- package/src/products/validate.ts +112 -0
- package/src/repo-validate/__tests__/validate-repo.test.ts +149 -0
- package/src/repo-validate/index.ts +2 -0
- package/src/repo-validate/types.ts +50 -0
- package/src/repo-validate/validate-repo.ts +196 -0
- package/src/requirement/__tests__/example.test.ts +24 -0
- package/src/requirement/__tests__/validate.test.ts +96 -0
- package/src/requirement/index.ts +4 -0
- package/src/requirement/types.ts +35 -0
- package/src/requirement/validate.ts +92 -0
- package/src/scenarios/__tests__/validate.test.ts +209 -0
- package/src/scenarios/index.ts +15 -0
- package/src/scenarios/types.ts +37 -0
- package/src/scenarios/validate.ts +122 -0
- package/src/schema-version.ts +16 -0
- package/src/scope.ts +46 -0
- package/src/stakeholder/__tests__/validate.test.ts +101 -0
- package/src/stakeholder/index.ts +3 -0
- package/src/stakeholder/types.ts +33 -0
- package/src/stakeholder/validate.ts +63 -0
- package/src/target-state/__tests__/validate.test.ts +74 -0
- package/src/target-state/index.ts +2 -0
- package/src/target-state/types.ts +28 -0
- package/src/target-state/validate.ts +67 -0
- package/src/theme/index.ts +4 -0
- package/src/theme/themes.ts +237 -0
- package/src/theme/tokens.ts +133 -0
- package/src/typed-id.ts +56 -0
- package/src/validation-types.ts +36 -0
- package/src/webview/__tests__/entry-notations.test.ts +77 -0
- package/src/webview/__tests__/entry.test.ts +64 -0
- package/src/webview/__tests__/render-goals.test.ts +72 -0
- package/src/webview/entry.ts +292 -0
- package/src/webview/render-activities.ts +156 -0
- package/src/webview/render-activity-card.ts +234 -0
- package/src/webview/render-applications.ts +123 -0
- package/src/webview/render-blocks.ts +91 -0
- package/src/webview/render-capability-map.ts +98 -0
- package/src/webview/render-fgca.ts +122 -0
- package/src/webview/render-goals.ts +110 -0
- package/src/webview/render-process-blueprint.ts +207 -0
- package/src/webview/render-process-map.ts +93 -0
- package/src/webview/render-products.ts +91 -0
- package/src/webview/render-scenarios.ts +90 -0
- package/src/webview/render-util.ts +34 -0
- package/src/webview/styles.css +323 -0
- package/src/yaml-normalize.ts +56 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Shared §3 envelope validator for standalone element primitives.
|
|
2
|
+
//
|
|
3
|
+
// Every standalone element file in the methodology canon carries the same
|
|
4
|
+
// envelope: a fixed `notation` tag, a canonical `id` whose TYPE prefix matches
|
|
5
|
+
// the element TYPE, `name`, an admission record (`zone`/`admitted_at`/
|
|
6
|
+
// `admitted_by`/`gate_checks`), and a primitive lifecycle
|
|
7
|
+
// (`valid_from`/`valid_to`). Per-TYPE fields (§7) sit between.
|
|
8
|
+
//
|
|
9
|
+
// Each per-TYPE validator delegates envelope checks here so the §3 rule lives
|
|
10
|
+
// in one place; per-TYPE fields (subtype enums, cross-references) are checked
|
|
11
|
+
// in the TYPE's own validator. The error `code` is parameterised so an
|
|
12
|
+
// envelope failure surfaces under the TYPE's own code (e.g. `FACTOR-001`,
|
|
13
|
+
// `CHANGE-001`), preserving per-TYPE attribution.
|
|
14
|
+
import { isCanonicalIdOfType } from './typed-id.js';
|
|
15
|
+
export function validateElementEnvelope(input, opts) {
|
|
16
|
+
const errors = [];
|
|
17
|
+
if (input === null || typeof input !== 'object' || Array.isArray(input)) {
|
|
18
|
+
errors.push({ code: opts.code, message: `${opts.idType} must be a YAML mapping.` });
|
|
19
|
+
return { record: null, errors };
|
|
20
|
+
}
|
|
21
|
+
const r = input;
|
|
22
|
+
if (r.notation !== opts.notation) {
|
|
23
|
+
errors.push({
|
|
24
|
+
code: opts.code,
|
|
25
|
+
message: `notation must be the fixed value "${opts.notation}".`,
|
|
26
|
+
path: 'notation',
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
if (!isCanonicalIdOfType(r.id, opts.idType)) {
|
|
30
|
+
errors.push({
|
|
31
|
+
code: opts.code,
|
|
32
|
+
message: `id "${String(r.id)}" must match ${opts.idType}-[<middle>-]<INTEGER>.`,
|
|
33
|
+
path: 'id',
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
for (const f of ['name', 'admitted_at', 'admitted_by', 'valid_from']) {
|
|
37
|
+
if (typeof r[f] !== 'string' || r[f].trim() === '') {
|
|
38
|
+
errors.push({ code: opts.code, message: `${f} is required.`, path: f });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (r.zone !== 'canon') {
|
|
42
|
+
errors.push({ code: opts.code, message: 'zone is required and must be "canon".', path: 'zone' });
|
|
43
|
+
}
|
|
44
|
+
if (r.gate_checks === null || typeof r.gate_checks !== 'object' || Array.isArray(r.gate_checks)) {
|
|
45
|
+
errors.push({ code: opts.code, message: 'gate_checks is required and must be a mapping.', path: 'gate_checks' });
|
|
46
|
+
}
|
|
47
|
+
if (!('valid_to' in r) || !(typeof r.valid_to === 'string' || r.valid_to === null)) {
|
|
48
|
+
errors.push({
|
|
49
|
+
code: opts.code,
|
|
50
|
+
message: 'valid_to is required (an ISO date string or null).',
|
|
51
|
+
path: 'valid_to',
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return { record: r, errors };
|
|
55
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { validateFactor } from '../validate.js';
|
|
3
|
+
function valid() {
|
|
4
|
+
return {
|
|
5
|
+
notation: 'factor',
|
|
6
|
+
id: 'FACTOR-EU-REG-1',
|
|
7
|
+
name: 'EU regulatory window for market entry',
|
|
8
|
+
type: 'external',
|
|
9
|
+
category: 'legal',
|
|
10
|
+
description: 'Standing external driver — the EU regulatory regime.',
|
|
11
|
+
references_constraint: ['CONSTRAINT-GDPR-RESIDENCY-1'],
|
|
12
|
+
zone: 'canon',
|
|
13
|
+
admitted_at: '2026-05-29',
|
|
14
|
+
admitted_by: 'v.korobeinikov',
|
|
15
|
+
gate_checks: { uniqueness: 'pass', consistency: 'pass', completeness: 'pass' },
|
|
16
|
+
valid_from: '2026-05-26',
|
|
17
|
+
valid_to: null,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const codes = (input) => validateFactor(input).errors.map(e => e.code);
|
|
21
|
+
const warns = (input) => validateFactor(input).warnings.map(w => w.code);
|
|
22
|
+
describe('validateFactor — positive', () => {
|
|
23
|
+
it('accepts the worked-example FACTOR-EU-REG-1', () => {
|
|
24
|
+
const r = validateFactor(valid());
|
|
25
|
+
expect(r.valid, JSON.stringify(r.errors)).toBe(true);
|
|
26
|
+
expect(r.errors).toHaveLength(0);
|
|
27
|
+
});
|
|
28
|
+
it('accepts a minimal factor (no type/category/references)', () => {
|
|
29
|
+
const r = valid();
|
|
30
|
+
delete r.type;
|
|
31
|
+
delete r.category;
|
|
32
|
+
delete r.references_constraint;
|
|
33
|
+
expect(validateFactor(r).valid).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe('validateFactor — FACTOR-001 (envelope / shape)', () => {
|
|
37
|
+
it('flags a non-object', () => {
|
|
38
|
+
expect(codes(null)).toEqual(['FACTOR-001']);
|
|
39
|
+
expect(codes(['x'])).toEqual(['FACTOR-001']);
|
|
40
|
+
});
|
|
41
|
+
it('flags a wrong notation tag', () => {
|
|
42
|
+
expect(codes({ ...valid(), notation: 'driver' })).toContain('FACTOR-001');
|
|
43
|
+
});
|
|
44
|
+
it('flags an id with the wrong TYPE prefix', () => {
|
|
45
|
+
expect(codes({ ...valid(), id: 'GOAL-EU-1' })).toContain('FACTOR-001');
|
|
46
|
+
});
|
|
47
|
+
it('flags an id with a leading-zero terminal', () => {
|
|
48
|
+
expect(codes({ ...valid(), id: 'FACTOR-EU-REG-01' })).toContain('FACTOR-001');
|
|
49
|
+
});
|
|
50
|
+
it('flags a missing valid_to key (distinct from a null value)', () => {
|
|
51
|
+
const r = valid();
|
|
52
|
+
delete r.valid_to;
|
|
53
|
+
expect(codes(r)).toContain('FACTOR-001');
|
|
54
|
+
expect(validateFactor({ ...valid(), valid_to: null }).valid).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
it('flags references_constraint entries that are not CONSTRAINT typed ids', () => {
|
|
57
|
+
expect(codes({ ...valid(), references_constraint: ['GOAL-EU-1'] })).toContain('FACTOR-001');
|
|
58
|
+
expect(codes({ ...valid(), references_constraint: 'CONSTRAINT-X-1' })).toContain('FACTOR-001');
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
describe('validateFactor — FACTOR-002 (type enum)', () => {
|
|
62
|
+
it('flags a type outside external/internal', () => {
|
|
63
|
+
expect(codes({ ...valid(), type: 'driver' })).toContain('FACTOR-002');
|
|
64
|
+
});
|
|
65
|
+
it('accepts each permitted type', () => {
|
|
66
|
+
for (const t of ['external', 'internal']) {
|
|
67
|
+
expect(codes({ ...valid(), type: t, category: undefined })).not.toContain('FACTOR-002');
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
describe('validateFactor — FACTOR-003 (PESTLE category)', () => {
|
|
72
|
+
it('flags a category outside the PESTLE classes', () => {
|
|
73
|
+
expect(codes({ ...valid(), category: 'cultural' })).toContain('FACTOR-003');
|
|
74
|
+
});
|
|
75
|
+
it('warns when category is set on an internal factor', () => {
|
|
76
|
+
expect(warns({ ...valid(), type: 'internal', category: 'economic' })).toContain('FACTOR-003');
|
|
77
|
+
});
|
|
78
|
+
it('accepts each permitted PESTLE class', () => {
|
|
79
|
+
for (const c of ['political', 'economic', 'social', 'technological', 'legal', 'environmental']) {
|
|
80
|
+
expect(codes({ ...valid(), category: c })).not.toContain('FACTOR-003');
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { GateChecks } from '../requirement/types.js';
|
|
2
|
+
/** Optional subtype — internal/external organisation force. */
|
|
3
|
+
export type FactorType = 'external' | 'internal';
|
|
4
|
+
export declare const FACTOR_TYPES: readonly ["external", "internal"];
|
|
5
|
+
/** PESTLE sub-classification — external factors only. Open-ended in practice
|
|
6
|
+
* but v1 fixes the six standard classes; an unknown category is flagged. */
|
|
7
|
+
export type FactorCategory = 'political' | 'economic' | 'social' | 'technological' | 'legal' | 'environmental';
|
|
8
|
+
export declare const FACTOR_CATEGORIES: readonly ["political", "economic", "social", "technological", "legal", "environmental"];
|
|
9
|
+
export interface Factor {
|
|
10
|
+
notation: 'factor';
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
type?: FactorType;
|
|
14
|
+
category?: FactorCategory;
|
|
15
|
+
description?: string;
|
|
16
|
+
references_constraint?: string[];
|
|
17
|
+
zone: 'canon';
|
|
18
|
+
admitted_at: string;
|
|
19
|
+
admitted_by: string;
|
|
20
|
+
gate_checks: GateChecks;
|
|
21
|
+
valid_from: string;
|
|
22
|
+
valid_to: string | null;
|
|
23
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// FACTOR — motivation-layer driver primitive.
|
|
2
|
+
// Schema: methodology notations/ELEMENT_PRIMITIVES.md §7.1 + envelope §3.
|
|
3
|
+
export const FACTOR_TYPES = ['external', 'internal'];
|
|
4
|
+
export const FACTOR_CATEGORIES = [
|
|
5
|
+
'political', 'economic', 'social', 'technological', 'legal', 'environmental',
|
|
6
|
+
];
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// FACTOR validator — methodology notations/ELEMENT_PRIMITIVES.md §7.1.
|
|
2
|
+
//
|
|
3
|
+
// Implements FACTOR-001..003. Cross-cutting HDR-/LIFECYCLE-/policy rules
|
|
4
|
+
// (CONTRACT.md §8) are owned elsewhere and out of scope for this
|
|
5
|
+
// single-artefact validator.
|
|
6
|
+
import { typeOfId } from '../typed-id.js';
|
|
7
|
+
import { validateElementEnvelope } from '../element-envelope.js';
|
|
8
|
+
import { FACTOR_CATEGORIES, FACTOR_TYPES } from './types.js';
|
|
9
|
+
export function validateFactor(input) {
|
|
10
|
+
const errors = [];
|
|
11
|
+
const warnings = [];
|
|
12
|
+
const { record, errors: envErrors } = validateElementEnvelope(input, {
|
|
13
|
+
notation: 'factor',
|
|
14
|
+
idType: 'FACTOR',
|
|
15
|
+
code: 'FACTOR-001',
|
|
16
|
+
});
|
|
17
|
+
errors.push(...envErrors);
|
|
18
|
+
if (!record)
|
|
19
|
+
return { valid: false, errors, warnings };
|
|
20
|
+
// FACTOR-002 — type subtype enum (optional field).
|
|
21
|
+
if (record.type !== undefined && !FACTOR_TYPES.includes(record.type)) {
|
|
22
|
+
errors.push({
|
|
23
|
+
code: 'FACTOR-002',
|
|
24
|
+
message: `type "${String(record.type)}" must be one of ${FACTOR_TYPES.join(', ')}.`,
|
|
25
|
+
path: 'type',
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
// FACTOR-003 — PESTLE category enum on external factors; warn on internal.
|
|
29
|
+
if (record.category !== undefined) {
|
|
30
|
+
if (!FACTOR_CATEGORIES.includes(record.category)) {
|
|
31
|
+
errors.push({
|
|
32
|
+
code: 'FACTOR-003',
|
|
33
|
+
message: `category "${String(record.category)}" must be a PESTLE class (${FACTOR_CATEGORIES.join(', ')}).`,
|
|
34
|
+
path: 'category',
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
else if (record.type === 'internal') {
|
|
38
|
+
// §7.1: "Omit on internal factors — PESTLE does not apply."
|
|
39
|
+
warnings.push({
|
|
40
|
+
code: 'FACTOR-003',
|
|
41
|
+
message: 'category is set on an internal factor; PESTLE applies to external drivers only.',
|
|
42
|
+
path: 'category',
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// FACTOR-001 — references_constraint shape + TYPE on each entry.
|
|
47
|
+
if (record.references_constraint !== undefined) {
|
|
48
|
+
if (!Array.isArray(record.references_constraint)) {
|
|
49
|
+
errors.push({
|
|
50
|
+
code: 'FACTOR-001',
|
|
51
|
+
message: 'references_constraint must be a list of typed CONSTRAINT IDs.',
|
|
52
|
+
path: 'references_constraint',
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
record.references_constraint.forEach((ref, i) => {
|
|
57
|
+
const type = typeOfId(ref);
|
|
58
|
+
if (type !== 'CONSTRAINT') {
|
|
59
|
+
errors.push({
|
|
60
|
+
code: 'FACTOR-001',
|
|
61
|
+
message: `references_constraint[${i}] "${String(ref)}" must be a CONSTRAINT typed ID.`,
|
|
62
|
+
path: `references_constraint[${i}]`,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
69
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { buildChainTable } from '../chain-table.js';
|
|
3
|
+
// Renders a ChainTable to a compact text grid for readable assertions. Each
|
|
4
|
+
// cell shows "label×rowSpan" (×1 omitted); a position covered by a span above
|
|
5
|
+
// shows "·".
|
|
6
|
+
function asGrid(t) {
|
|
7
|
+
return t.rows.map(row => row.map(c => {
|
|
8
|
+
if (c === null)
|
|
9
|
+
return '·';
|
|
10
|
+
const label = c.cell ? c.cell.label : '∅';
|
|
11
|
+
return c.rowSpan > 1 ? `${label}×${c.rowSpan}` : label;
|
|
12
|
+
}));
|
|
13
|
+
}
|
|
14
|
+
describe('buildChainTable — worked example (FGCA)', () => {
|
|
15
|
+
// F1 → G1, G2 ; G1 → C1 → A1 ; G2 → C2 → A2, A3
|
|
16
|
+
const doc = {
|
|
17
|
+
factors: [{ id: 1, name: 'F1' }],
|
|
18
|
+
goals: [
|
|
19
|
+
{ id: 10, name: 'G1', factor: [{ id: 1 }] },
|
|
20
|
+
{ id: 11, name: 'G2', factor: [{ id: 1 }] },
|
|
21
|
+
],
|
|
22
|
+
changes: [
|
|
23
|
+
{ id: 20, name: 'C1', goal_id: 10, activity_ids: [30] },
|
|
24
|
+
{ id: 21, name: 'C2', goal_id: 11, activity_ids: [31, 32] },
|
|
25
|
+
],
|
|
26
|
+
activities: [
|
|
27
|
+
{ id: 30, name: 'A1', goal_id: 10 },
|
|
28
|
+
{ id: 31, name: 'A2', goal_id: 11 },
|
|
29
|
+
{ id: 32, name: 'A3', goal_id: 11 },
|
|
30
|
+
],
|
|
31
|
+
};
|
|
32
|
+
it('produces the spec grid with F1 spanning 3 and G2/C2 spanning 2', () => {
|
|
33
|
+
const t = buildChainTable(doc);
|
|
34
|
+
expect(t.columns).toEqual(['factor', 'goal', 'change', 'activity']);
|
|
35
|
+
expect(asGrid(t)).toEqual([
|
|
36
|
+
['F1×3', 'G1', 'C1', 'A1'],
|
|
37
|
+
['·', 'G2×2', 'C2×2', 'A2'],
|
|
38
|
+
['·', '·', '·', 'A3'],
|
|
39
|
+
]);
|
|
40
|
+
});
|
|
41
|
+
it('orders the merged cells with correct rowSpans on the model', () => {
|
|
42
|
+
const t = buildChainTable(doc);
|
|
43
|
+
// Factor cell spans all three rows.
|
|
44
|
+
expect(t.rows[0][0]).toMatchObject({ rowSpan: 3, cell: { label: 'F1' } });
|
|
45
|
+
// Covered positions are null (no <td> emitted).
|
|
46
|
+
expect(t.rows[1][0]).toBeNull();
|
|
47
|
+
expect(t.rows[2][1]).toBeNull();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe('buildChainTable — FGA (no Change column)', () => {
|
|
51
|
+
// FGA links Goal → Activity directly via activity.goal_id; no changes.
|
|
52
|
+
const doc = {
|
|
53
|
+
factors: [{ id: 1, name: 'F1' }],
|
|
54
|
+
goals: [{ id: 10, name: 'G1', factor: [{ id: 1 }] }],
|
|
55
|
+
activities: [
|
|
56
|
+
{ id: 30, name: 'A1', goal_id: 10 },
|
|
57
|
+
{ id: 31, name: 'A2', goal_id: 10 },
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
it('emits three columns and merges Factor/Goal across the two activities', () => {
|
|
61
|
+
const t = buildChainTable(doc, { hideChanges: true });
|
|
62
|
+
expect(t.columns).toEqual(['factor', 'goal', 'activity']);
|
|
63
|
+
expect(asGrid(t)).toEqual([
|
|
64
|
+
['F1×2', 'G1×2', 'A1'],
|
|
65
|
+
['·', '·', 'A2'],
|
|
66
|
+
]);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe('buildChainTable — missing downstream links (gaps stay visible)', () => {
|
|
70
|
+
it('shows a factor with no goal, a goal with no change, and a change with no activity', () => {
|
|
71
|
+
const doc = {
|
|
72
|
+
factors: [
|
|
73
|
+
{ id: 1, name: 'F1' },
|
|
74
|
+
{ id: 2, name: 'F2' }, // no goals
|
|
75
|
+
],
|
|
76
|
+
goals: [
|
|
77
|
+
{ id: 10, name: 'G1', factor: [{ id: 1 }] }, // no change, no activity
|
|
78
|
+
{ id: 11, name: 'G2', factor: [{ id: 1 }] }, // has a change with no activity
|
|
79
|
+
],
|
|
80
|
+
changes: [{ id: 20, name: 'C1', goal_id: 11, activity_ids: [] }],
|
|
81
|
+
activities: [],
|
|
82
|
+
};
|
|
83
|
+
const t = buildChainTable(doc);
|
|
84
|
+
expect(asGrid(t)).toEqual([
|
|
85
|
+
['F1×2', 'G1', '∅', '∅'],
|
|
86
|
+
['·', 'G2', 'C1', '∅'],
|
|
87
|
+
['F2', '∅', '∅', '∅'],
|
|
88
|
+
]);
|
|
89
|
+
});
|
|
90
|
+
it('renders a change-less direct goal→activity path with an empty Change cell', () => {
|
|
91
|
+
const doc = {
|
|
92
|
+
factors: [{ id: 1, name: 'F1' }],
|
|
93
|
+
goals: [{ id: 10, name: 'G1', factor: [{ id: 1 }] }],
|
|
94
|
+
// activity bound straight to the goal, not covered by any change
|
|
95
|
+
changes: [],
|
|
96
|
+
activities: [{ id: 30, name: 'A1', goal_id: 10 }],
|
|
97
|
+
};
|
|
98
|
+
const t = buildChainTable(doc);
|
|
99
|
+
expect(asGrid(t)).toEqual([['F1', 'G1', '∅', 'A1']]);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
describe('buildChainTable — orphans (broken refs are warnings, not errors)', () => {
|
|
103
|
+
it('places a goal with a missing factor in a trailing empty-Factor row', () => {
|
|
104
|
+
const doc = {
|
|
105
|
+
factors: [{ id: 1, name: 'F1' }],
|
|
106
|
+
goals: [
|
|
107
|
+
{ id: 10, name: 'G1', factor: [{ id: 1 }] },
|
|
108
|
+
{ id: 11, name: 'Gx', factor: [{ id: 99 }] }, // factor 99 does not exist
|
|
109
|
+
{ id: 12, name: 'Gy' }, // no factor at all
|
|
110
|
+
],
|
|
111
|
+
changes: [{ id: 20, name: 'C1', goal_id: 10, activity_ids: [30] }],
|
|
112
|
+
activities: [{ id: 30, name: 'A1', goal_id: 10 }],
|
|
113
|
+
};
|
|
114
|
+
const t = buildChainTable(doc);
|
|
115
|
+
expect(asGrid(t)).toEqual([
|
|
116
|
+
['F1', 'G1', 'C1', 'A1'],
|
|
117
|
+
['∅×2', 'Gx', '∅', '∅'],
|
|
118
|
+
['·', 'Gy', '∅', '∅'],
|
|
119
|
+
]);
|
|
120
|
+
});
|
|
121
|
+
it('surfaces an unlinked activity as a trailing row with empty cells to its left', () => {
|
|
122
|
+
const doc = {
|
|
123
|
+
factors: [{ id: 1, name: 'F1' }],
|
|
124
|
+
goals: [{ id: 10, name: 'G1', factor: [{ id: 1 }] }],
|
|
125
|
+
changes: [{ id: 20, name: 'C1', goal_id: 10, activity_ids: [30] }],
|
|
126
|
+
activities: [
|
|
127
|
+
{ id: 30, name: 'A1', goal_id: 10 },
|
|
128
|
+
{ id: 99, name: 'Aorphan' }, // no goal, no change
|
|
129
|
+
],
|
|
130
|
+
};
|
|
131
|
+
const t = buildChainTable(doc);
|
|
132
|
+
expect(asGrid(t)).toEqual([
|
|
133
|
+
['F1', 'G1', 'C1', 'A1'],
|
|
134
|
+
['∅', '∅', '∅', 'Aorphan'],
|
|
135
|
+
]);
|
|
136
|
+
});
|
|
137
|
+
it('surfaces a change whose goal is missing as an empty Factor+Goal row', () => {
|
|
138
|
+
const doc = {
|
|
139
|
+
factors: [],
|
|
140
|
+
goals: [],
|
|
141
|
+
changes: [{ id: 20, name: 'Cx', goal_id: 999, activity_ids: [30] }],
|
|
142
|
+
activities: [{ id: 30, name: 'A1' }],
|
|
143
|
+
};
|
|
144
|
+
const t = buildChainTable(doc);
|
|
145
|
+
expect(asGrid(t)).toEqual([['∅', '∅', 'Cx', 'A1']]);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
describe('buildChainTable — name fallback', () => {
|
|
149
|
+
it('falls back to the id when an element has an empty name', () => {
|
|
150
|
+
const doc = {
|
|
151
|
+
factors: [{ id: 7, name: '' }],
|
|
152
|
+
goals: [],
|
|
153
|
+
activities: [],
|
|
154
|
+
};
|
|
155
|
+
const t = buildChainTable(doc);
|
|
156
|
+
expect(t.rows[0][0]?.cell?.label).toBe('7');
|
|
157
|
+
});
|
|
158
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeAll } from "vitest";
|
|
2
|
+
// reactflow uses browser APIs — stub the minimum required before importing layout
|
|
3
|
+
beforeAll(() => {
|
|
4
|
+
vi.mock("reactflow", () => ({
|
|
5
|
+
Position: { Left: "left", Right: "right" },
|
|
6
|
+
MarkerType: { ArrowClosed: "arrowclosed" },
|
|
7
|
+
}));
|
|
8
|
+
});
|
|
9
|
+
import { buildFGCALayout } from "../layout";
|
|
10
|
+
import { ALL_FGCA_COLUMNS } from "../types";
|
|
11
|
+
const ALL_COLS = new Set(ALL_FGCA_COLUMNS);
|
|
12
|
+
const factors = [
|
|
13
|
+
{ id: 1, name: "Digital growth" },
|
|
14
|
+
{ id: 2, name: "Talent shortage" },
|
|
15
|
+
];
|
|
16
|
+
const goals = [
|
|
17
|
+
{ id: 10, name: "Grow revenue", level: 0, factor: [{ id: 1 }] },
|
|
18
|
+
{ id: 11, name: "Build talent pipeline", level: 1, factor: [{ id: 2 }] },
|
|
19
|
+
];
|
|
20
|
+
const changes = [
|
|
21
|
+
{ id: 100, name: "Launch digital channel", goal_id: 10, activity_ids: [200] },
|
|
22
|
+
{ id: 101, name: "Partner with bootcamps", goal_id: 11, activity_ids: [201] },
|
|
23
|
+
];
|
|
24
|
+
const activities = [
|
|
25
|
+
{ id: 200, name: "Build e-commerce MVP", goal_id: 10, activity_type_id: 1 },
|
|
26
|
+
{ id: 201, name: "Run hiring sprint", goal_id: 11, activity_type_id: 1 },
|
|
27
|
+
];
|
|
28
|
+
describe("buildFGCALayout — all columns visible", () => {
|
|
29
|
+
const { nodes, edges } = buildFGCALayout({
|
|
30
|
+
factors,
|
|
31
|
+
goals,
|
|
32
|
+
changes,
|
|
33
|
+
activities,
|
|
34
|
+
visibleColumns: ALL_COLS,
|
|
35
|
+
activityTypeList: [{ id: 1, name: "Initiative" }],
|
|
36
|
+
});
|
|
37
|
+
it("produces the correct number of nodes", () => {
|
|
38
|
+
expect(nodes).toHaveLength(factors.length + goals.length + changes.length + activities.length);
|
|
39
|
+
});
|
|
40
|
+
it("assigns correct node types", () => {
|
|
41
|
+
const types = nodes.map((n) => n.type);
|
|
42
|
+
expect(types.filter((t) => t === "fgcaFactor")).toHaveLength(factors.length);
|
|
43
|
+
expect(types.filter((t) => t === "fgcaGoal")).toHaveLength(goals.length);
|
|
44
|
+
expect(types.filter((t) => t === "fgcaChange")).toHaveLength(changes.length);
|
|
45
|
+
expect(types.filter((t) => t === "fgcaActivity")).toHaveLength(activities.length);
|
|
46
|
+
});
|
|
47
|
+
it("creates Factor→Goal edges", () => {
|
|
48
|
+
const fgEdges = edges.filter((e) => e.source.startsWith("factor_") && e.target.startsWith("goal_"));
|
|
49
|
+
expect(fgEdges).toHaveLength(2);
|
|
50
|
+
expect(fgEdges.find((e) => e.source === "factor_1" && e.target === "goal_10")).toBeTruthy();
|
|
51
|
+
});
|
|
52
|
+
it("creates Goal→Change edges", () => {
|
|
53
|
+
const gcEdges = edges.filter((e) => e.source.startsWith("goal_") && e.target.startsWith("change_"));
|
|
54
|
+
expect(gcEdges).toHaveLength(2);
|
|
55
|
+
});
|
|
56
|
+
it("creates Change→Activity edges", () => {
|
|
57
|
+
const caEdges = edges.filter((e) => e.source.startsWith("change_") && e.target.startsWith("activity_"));
|
|
58
|
+
expect(caEdges).toHaveLength(2);
|
|
59
|
+
});
|
|
60
|
+
it("does NOT create direct Goal→Activity edges when Change column is visible and all activities are covered", () => {
|
|
61
|
+
const directEdges = edges.filter((e) => e.source.startsWith("goal_") && e.target.startsWith("activity_"));
|
|
62
|
+
expect(directEdges).toHaveLength(0);
|
|
63
|
+
});
|
|
64
|
+
it("uses the activity type name in activity node data", () => {
|
|
65
|
+
const actNode = nodes.find((n) => n.id === "activity_200");
|
|
66
|
+
expect(actNode?.data.activityTypeName).toBe("Initiative");
|
|
67
|
+
});
|
|
68
|
+
it("encodes F-XXXX label format via id in node data", () => {
|
|
69
|
+
const factorNode = nodes.find((n) => n.id === "factor_1");
|
|
70
|
+
expect(String(factorNode?.data.id).padStart(4, "0")).toBe("0001");
|
|
71
|
+
});
|
|
72
|
+
it("encodes C-XXX label format via id in node data", () => {
|
|
73
|
+
const changeNode = nodes.find((n) => n.id === "change_100");
|
|
74
|
+
expect(String(changeNode?.data.id).padStart(3, "0")).toBe("100");
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
describe("buildFGCALayout — Factor column hidden", () => {
|
|
78
|
+
const cols = new Set(ALL_FGCA_COLUMNS.filter((c) => c !== "factor"));
|
|
79
|
+
const { nodes, edges } = buildFGCALayout({ factors, goals, changes, activities, visibleColumns: cols });
|
|
80
|
+
it("produces no factor nodes", () => {
|
|
81
|
+
expect(nodes.filter((n) => n.type === "fgcaFactor")).toHaveLength(0);
|
|
82
|
+
});
|
|
83
|
+
it("produces no Factor→Goal edges", () => {
|
|
84
|
+
expect(edges.filter((e) => e.source.startsWith("factor_"))).toHaveLength(0);
|
|
85
|
+
});
|
|
86
|
+
it("still produces Goal and downstream nodes", () => {
|
|
87
|
+
expect(nodes.filter((n) => n.type === "fgcaGoal")).toHaveLength(goals.length);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
describe("buildFGCALayout — Change column hidden", () => {
|
|
91
|
+
const cols = new Set(ALL_FGCA_COLUMNS.filter((c) => c !== "change"));
|
|
92
|
+
const { nodes, edges } = buildFGCALayout({ factors, goals, changes, activities, visibleColumns: cols });
|
|
93
|
+
it("produces no change nodes", () => {
|
|
94
|
+
expect(nodes.filter((n) => n.type === "fgcaChange")).toHaveLength(0);
|
|
95
|
+
});
|
|
96
|
+
it("creates direct Goal→Activity edges when Change column is hidden", () => {
|
|
97
|
+
const directEdges = edges.filter((e) => e.source.startsWith("goal_") && e.target.startsWith("activity_"));
|
|
98
|
+
expect(directEdges.length).toBeGreaterThan(0);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
describe("buildFGCALayout — only one column visible", () => {
|
|
102
|
+
const { nodes, edges } = buildFGCALayout({
|
|
103
|
+
factors,
|
|
104
|
+
goals,
|
|
105
|
+
changes,
|
|
106
|
+
activities,
|
|
107
|
+
visibleColumns: new Set(["goal"]),
|
|
108
|
+
});
|
|
109
|
+
it("produces only goal nodes", () => {
|
|
110
|
+
expect(nodes.filter((n) => n.type !== "fgcaGoal")).toHaveLength(0);
|
|
111
|
+
});
|
|
112
|
+
it("produces no edges", () => {
|
|
113
|
+
expect(edges).toHaveLength(0);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
describe("buildFGCALayout — columns are positioned left to right", () => {
|
|
117
|
+
const { nodes } = buildFGCALayout({ factors, goals, changes, activities, visibleColumns: ALL_COLS });
|
|
118
|
+
it("factor column x < goal column x", () => {
|
|
119
|
+
const f = nodes.find((n) => n.type === "fgcaFactor");
|
|
120
|
+
const g = nodes.find((n) => n.type === "fgcaGoal");
|
|
121
|
+
expect(f.position.x).toBeLessThan(g.position.x);
|
|
122
|
+
});
|
|
123
|
+
it("goal column x < change column x", () => {
|
|
124
|
+
const g = nodes.find((n) => n.type === "fgcaGoal");
|
|
125
|
+
const c = nodes.find((n) => n.type === "fgcaChange");
|
|
126
|
+
expect(g.position.x).toBeLessThan(c.position.x);
|
|
127
|
+
});
|
|
128
|
+
it("change column x < activity column x", () => {
|
|
129
|
+
const c = nodes.find((n) => n.type === "fgcaChange");
|
|
130
|
+
const a = nodes.find((n) => n.type === "fgcaActivity");
|
|
131
|
+
expect(c.position.x).toBeLessThan(a.position.x);
|
|
132
|
+
});
|
|
133
|
+
// Pre-release blocker regression (orchestrator review 2026-05-21).
|
|
134
|
+
it("[blocker] tolerates a change with no activity_ids (optional per validator)", () => {
|
|
135
|
+
const changesNoIds = [
|
|
136
|
+
{ id: 100, name: "No activities", goal_id: 10 },
|
|
137
|
+
];
|
|
138
|
+
expect(() => buildFGCALayout({ factors, goals, changes: changesNoIds, activities, visibleColumns: ALL_COLS })).not.toThrow();
|
|
139
|
+
});
|
|
140
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|