@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,326 @@
|
|
|
1
|
+
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
2
|
+
const VALID_WEEKDAYS = new Set(['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']);
|
|
3
|
+
export function validateActivities(input) {
|
|
4
|
+
const errors = [];
|
|
5
|
+
const warnings = [];
|
|
6
|
+
if (!input || typeof input !== 'object') {
|
|
7
|
+
return { valid: false, errors: [{ code: 'ACT-001', message: 'Input must be an object' }], warnings };
|
|
8
|
+
}
|
|
9
|
+
const raw = input;
|
|
10
|
+
// ACT-001: notation must equal "activities"
|
|
11
|
+
if (raw.notation === undefined) {
|
|
12
|
+
errors.push({ code: 'ACT-001', message: 'notation field is required' });
|
|
13
|
+
return { valid: false, errors, warnings };
|
|
14
|
+
}
|
|
15
|
+
if (raw.notation !== 'activities') {
|
|
16
|
+
errors.push({ code: 'ACT-001', message: `notation must be "activities", got "${String(raw.notation)}"` });
|
|
17
|
+
return { valid: false, errors, warnings };
|
|
18
|
+
}
|
|
19
|
+
if (!Array.isArray(raw.activities)) {
|
|
20
|
+
errors.push({ code: 'SCHEMA_INVALID', message: 'activities must be an array', path: 'activities' });
|
|
21
|
+
return { valid: false, errors, warnings };
|
|
22
|
+
}
|
|
23
|
+
// ACT-014 / ACT-015: project.calendar validation (run before per-activity loop;
|
|
24
|
+
// surfaces clearly even when activities also have issues).
|
|
25
|
+
if (raw.project && typeof raw.project === 'object') {
|
|
26
|
+
const project = raw.project;
|
|
27
|
+
const cal = project.calendar;
|
|
28
|
+
if (cal && typeof cal === 'object') {
|
|
29
|
+
const calendar = cal;
|
|
30
|
+
if (calendar.working_days !== undefined) {
|
|
31
|
+
if (!Array.isArray(calendar.working_days)) {
|
|
32
|
+
errors.push({ code: 'ACT-014', message: 'project.calendar.working_days must be an array of weekday names', path: 'project.calendar.working_days' });
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
const seen = new Set();
|
|
36
|
+
for (let i = 0; i < calendar.working_days.length; i++) {
|
|
37
|
+
const raw = calendar.working_days[i];
|
|
38
|
+
const day = typeof raw === 'string' ? raw.toLowerCase() : '';
|
|
39
|
+
if (!VALID_WEEKDAYS.has(day)) {
|
|
40
|
+
errors.push({
|
|
41
|
+
code: 'ACT-014',
|
|
42
|
+
message: `project.calendar.working_days[${i}] "${String(raw)}" must be one of: mon, tue, wed, thu, fri, sat, sun`,
|
|
43
|
+
path: `project.calendar.working_days[${i}]`,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
else if (seen.has(day)) {
|
|
47
|
+
errors.push({
|
|
48
|
+
code: 'ACT-014',
|
|
49
|
+
message: `project.calendar.working_days has duplicate entry "${day}"`,
|
|
50
|
+
path: `project.calendar.working_days[${i}]`,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
seen.add(day);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (calendar.holidays !== undefined) {
|
|
60
|
+
if (!Array.isArray(calendar.holidays)) {
|
|
61
|
+
errors.push({ code: 'ACT-015', message: 'project.calendar.holidays must be an array of ISO 8601 dates', path: 'project.calendar.holidays' });
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
for (let i = 0; i < calendar.holidays.length; i++) {
|
|
65
|
+
const h = calendar.holidays[i];
|
|
66
|
+
if (typeof h !== 'string' || !DATE_RE.test(h)) {
|
|
67
|
+
errors.push({
|
|
68
|
+
code: 'ACT-015',
|
|
69
|
+
message: `project.calendar.holidays[${i}] "${String(h)}" must be an ISO 8601 date (YYYY-MM-DD)`,
|
|
70
|
+
path: `project.calendar.holidays[${i}]`,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const doc = raw;
|
|
79
|
+
const idSet = new Set();
|
|
80
|
+
for (let i = 0; i < doc.activities.length; i++) {
|
|
81
|
+
const a = doc.activities[i];
|
|
82
|
+
const path = `activities[${i}]`;
|
|
83
|
+
if (!a || typeof a !== 'object') {
|
|
84
|
+
errors.push({ code: 'SCHEMA_INVALID', message: 'activity entry must be an object', path });
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const act = a;
|
|
88
|
+
// ACT-010: reject single-value forms
|
|
89
|
+
if ('goal' in act) {
|
|
90
|
+
errors.push({ code: 'ACT-010', message: `Use "goals: []" array form instead of "goal:" (activity at index ${i})`, path });
|
|
91
|
+
}
|
|
92
|
+
if ('predecessor' in act) {
|
|
93
|
+
errors.push({ code: 'ACT-010', message: `Use "predecessors: []" array form instead of "predecessor:" (activity at index ${i})`, path });
|
|
94
|
+
}
|
|
95
|
+
if ('tag' in act) {
|
|
96
|
+
errors.push({ code: 'ACT-010', message: `Use "tags: []" array form instead of "tag:" (activity at index ${i})`, path });
|
|
97
|
+
}
|
|
98
|
+
// ACT-002: non-empty id
|
|
99
|
+
if (!act.id || typeof act.id !== 'string' || act.id.trim() === '') {
|
|
100
|
+
errors.push({ code: 'ACT-002', message: `Activity at index ${i} is missing a non-empty id`, path });
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const id = act.id;
|
|
104
|
+
// ACT-003: non-empty name
|
|
105
|
+
if (!act.name || typeof act.name !== 'string' || act.name.trim() === '') {
|
|
106
|
+
errors.push({ code: 'ACT-003', message: `Activity "${id}" is missing a non-empty name`, path });
|
|
107
|
+
}
|
|
108
|
+
// ACT-004: unique ids
|
|
109
|
+
if (idSet.has(id)) {
|
|
110
|
+
errors.push({ code: 'ACT-004', message: `Duplicate activity id: "${id}"`, path });
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
idSet.add(id);
|
|
114
|
+
}
|
|
115
|
+
// ACT-009: non-negative numeric fields
|
|
116
|
+
const numericFields = ['duration', 'labor_cost', 'resources_cost', 'effort', 'score', 'sort'];
|
|
117
|
+
for (const field of numericFields) {
|
|
118
|
+
const val = act[field];
|
|
119
|
+
if (val !== undefined && val !== null) {
|
|
120
|
+
if (typeof val !== 'number' || val < 0) {
|
|
121
|
+
errors.push({ code: 'ACT-009', message: `Activity "${id}" field "${field}" must be a non-negative number, got ${String(val)}`, path });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// ACT-011: warn if no duration
|
|
126
|
+
if (act.duration === undefined || act.duration === null) {
|
|
127
|
+
warnings.push({ code: 'ACT-011', message: `Activity "${id}" has no duration — cannot participate in CPM analysis`, path });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (errors.length > 0)
|
|
131
|
+
return { valid: false, errors, warnings };
|
|
132
|
+
// Second pass — checks that need the full id set
|
|
133
|
+
for (let i = 0; i < doc.activities.length; i++) {
|
|
134
|
+
const a = doc.activities[i];
|
|
135
|
+
const path = `activities[${i}]`;
|
|
136
|
+
// ACT-007: self-loop
|
|
137
|
+
if (Array.isArray(a.predecessors) && a.predecessors.includes(a.id)) {
|
|
138
|
+
errors.push({ code: 'ACT-007', message: `Activity "${a.id}" lists itself as a predecessor`, path });
|
|
139
|
+
}
|
|
140
|
+
// ACT-005: predecessor existence (intra-document)
|
|
141
|
+
for (const predId of (a.predecessors ?? [])) {
|
|
142
|
+
if (!idSet.has(predId)) {
|
|
143
|
+
errors.push({ code: 'ACT-005', message: `Activity "${a.id}" references unknown predecessor "${predId}"`, path });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// ACT-008: date format validation + end_date >= start_date.
|
|
147
|
+
// The orchestrator's pre-release review flagged that the raw lexicographic
|
|
148
|
+
// string compare is only correct for strict YYYY-MM-DD, with no format
|
|
149
|
+
// check applied. Now we validate the format first; the order compare runs
|
|
150
|
+
// only when both dates are valid (otherwise the comparison message would
|
|
151
|
+
// be misleading).
|
|
152
|
+
let startValid = true;
|
|
153
|
+
let endValid = true;
|
|
154
|
+
if (a.start_date !== undefined) {
|
|
155
|
+
if (typeof a.start_date !== 'string' || !DATE_RE.test(a.start_date)) {
|
|
156
|
+
errors.push({ code: 'ACT-008', message: `Activity "${a.id}" start_date "${String(a.start_date)}" must be ISO 8601 YYYY-MM-DD`, path });
|
|
157
|
+
startValid = false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (a.end_date !== undefined) {
|
|
161
|
+
if (typeof a.end_date !== 'string' || !DATE_RE.test(a.end_date)) {
|
|
162
|
+
errors.push({ code: 'ACT-008', message: `Activity "${a.id}" end_date "${String(a.end_date)}" must be ISO 8601 YYYY-MM-DD`, path });
|
|
163
|
+
endValid = false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (a.start_date && a.end_date && startValid && endValid) {
|
|
167
|
+
if (a.end_date < a.start_date) {
|
|
168
|
+
errors.push({ code: 'ACT-008', message: `Activity "${a.id}" end_date "${a.end_date}" is before start_date "${a.start_date}"`, path });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (errors.length > 0)
|
|
173
|
+
return { valid: false, errors, warnings };
|
|
174
|
+
// ACT-006: no cycles
|
|
175
|
+
const cycleError = detectCycle(doc.activities);
|
|
176
|
+
if (cycleError)
|
|
177
|
+
errors.push(cycleError);
|
|
178
|
+
if (errors.length > 0)
|
|
179
|
+
return { valid: false, errors, warnings };
|
|
180
|
+
// ACT-012: date/duration consistency (warning only)
|
|
181
|
+
for (let i = 0; i < doc.activities.length; i++) {
|
|
182
|
+
const a = doc.activities[i];
|
|
183
|
+
if (a.start_date && a.end_date && a.duration !== undefined) {
|
|
184
|
+
const start = new Date(a.start_date).getTime();
|
|
185
|
+
const end = new Date(a.end_date).getTime();
|
|
186
|
+
const diffDays = Math.round((end - start) / 86400000);
|
|
187
|
+
if (Math.abs(diffDays - a.duration) > 1) {
|
|
188
|
+
warnings.push({
|
|
189
|
+
code: 'ACT-012',
|
|
190
|
+
message: `Activity "${a.id}" duration ${a.duration} is inconsistent with date range of ~${diffDays} days`,
|
|
191
|
+
path: `activities[${i}]`,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// ACT-013: orphan activities (warning only)
|
|
197
|
+
const successorIds = new Set();
|
|
198
|
+
for (const a of doc.activities) {
|
|
199
|
+
for (const pred of (a.predecessors ?? []))
|
|
200
|
+
successorIds.add(pred);
|
|
201
|
+
}
|
|
202
|
+
for (let i = 0; i < doc.activities.length; i++) {
|
|
203
|
+
const a = doc.activities[i];
|
|
204
|
+
const hasSuccessors = successorIds.has(a.id);
|
|
205
|
+
const hasGoals = Array.isArray(a.goals) && a.goals.length > 0;
|
|
206
|
+
const hasPredecessors = Array.isArray(a.predecessors) && a.predecessors.length > 0;
|
|
207
|
+
if (doc.activities.length > 1 && !hasSuccessors && !hasGoals && !hasPredecessors) {
|
|
208
|
+
warnings.push({
|
|
209
|
+
code: 'ACT-013',
|
|
210
|
+
message: `Activity "${a.id}" appears to be structurally orphan (no predecessors, successors, or goals)`,
|
|
211
|
+
path: `activities[${i}]`,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// ACT-016: milestone (duration=0) with both pinned dates MUST have start == end.
|
|
216
|
+
for (let i = 0; i < doc.activities.length; i++) {
|
|
217
|
+
const a = doc.activities[i];
|
|
218
|
+
if (a.duration === 0 && a.start_date && a.end_date && a.start_date !== a.end_date) {
|
|
219
|
+
errors.push({
|
|
220
|
+
code: 'ACT-016',
|
|
221
|
+
message: `Milestone "${a.id}" has duration 0 but start_date "${a.start_date}" ≠ end_date "${a.end_date}"`,
|
|
222
|
+
path: `activities[${i}]`,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (errors.length > 0)
|
|
227
|
+
return { valid: false, errors, warnings };
|
|
228
|
+
// ACT-017 / ACT-018: phase warnings. A "phase" is an activity that is referenced
|
|
229
|
+
// by another activity's `parent` field. ACT-017 warns when a phase carries its
|
|
230
|
+
// own duration/dates (those roll up from children). ACT-018 warns when an
|
|
231
|
+
// activity that looks like a phase (no duration, no pinned dates) has no
|
|
232
|
+
// children — possibly an unused placeholder.
|
|
233
|
+
const childCount = new Map();
|
|
234
|
+
for (const a of doc.activities) {
|
|
235
|
+
if (typeof a.parent === 'string' && a.parent.length > 0) {
|
|
236
|
+
childCount.set(a.parent, (childCount.get(a.parent) ?? 0) + 1);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
for (let i = 0; i < doc.activities.length; i++) {
|
|
240
|
+
const a = doc.activities[i];
|
|
241
|
+
const isReferencedAsParent = (childCount.get(a.id) ?? 0) > 0;
|
|
242
|
+
if (isReferencedAsParent) {
|
|
243
|
+
const carriesOwnTiming = (a.duration !== undefined && a.duration !== null) ||
|
|
244
|
+
typeof a.start_date === 'string' ||
|
|
245
|
+
typeof a.end_date === 'string';
|
|
246
|
+
if (carriesOwnTiming) {
|
|
247
|
+
warnings.push({
|
|
248
|
+
code: 'ACT-017',
|
|
249
|
+
message: `Phase "${a.id}" carries its own duration/dates — those should roll up from children, not be authored directly`,
|
|
250
|
+
path: `activities[${i}]`,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
// Not referenced as parent. ACT-018 only fires when the activity LOOKS
|
|
256
|
+
// like a phase: no duration declared and no pinned dates. A leaf without
|
|
257
|
+
// duration is already covered by ACT-011 — the same activity may surface
|
|
258
|
+
// both warnings, which is correct (separate concerns: CPM-unfit vs
|
|
259
|
+
// empty-phase).
|
|
260
|
+
const looksLikePhase = (a.duration === undefined || a.duration === null) &&
|
|
261
|
+
!a.start_date &&
|
|
262
|
+
!a.end_date;
|
|
263
|
+
if (looksLikePhase) {
|
|
264
|
+
warnings.push({
|
|
265
|
+
code: 'ACT-018',
|
|
266
|
+
message: `Activity "${a.id}" has no duration and no children — may be an empty phase`,
|
|
267
|
+
path: `activities[${i}]`,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// ACT-019: Gantt view will not render when neither project.start_date nor
|
|
273
|
+
// any per-activity pinned date pair is present. Network view is unaffected.
|
|
274
|
+
const hasProjectStart = typeof doc.project?.start_date === 'string' && doc.project.start_date.length > 0;
|
|
275
|
+
const hasPinnedActivity = doc.activities.some((a) => typeof a.start_date === 'string' && typeof a.end_date === 'string');
|
|
276
|
+
if (!hasProjectStart && !hasPinnedActivity) {
|
|
277
|
+
warnings.push({
|
|
278
|
+
code: 'ACT-019',
|
|
279
|
+
message: 'Gantt view will not render: project.start_date is absent and no activity has both start_date and end_date pinned. Network view is unaffected.',
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
283
|
+
}
|
|
284
|
+
function detectCycle(activities) {
|
|
285
|
+
const successors = new Map();
|
|
286
|
+
for (const a of activities) {
|
|
287
|
+
successors.set(a.id, []);
|
|
288
|
+
}
|
|
289
|
+
for (const a of activities) {
|
|
290
|
+
for (const pred of (a.predecessors ?? [])) {
|
|
291
|
+
const list = successors.get(pred);
|
|
292
|
+
if (list)
|
|
293
|
+
list.push(a.id);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
// Kahn's algorithm: if we can't process all nodes, there's a cycle
|
|
297
|
+
const inDegree = new Map();
|
|
298
|
+
for (const a of activities)
|
|
299
|
+
inDegree.set(a.id, 0);
|
|
300
|
+
for (const a of activities) {
|
|
301
|
+
for (const pred of (a.predecessors ?? [])) {
|
|
302
|
+
inDegree.set(a.id, (inDegree.get(a.id) ?? 0) + 1);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const queue = [];
|
|
306
|
+
for (const [id, deg] of inDegree) {
|
|
307
|
+
if (deg === 0)
|
|
308
|
+
queue.push(id);
|
|
309
|
+
}
|
|
310
|
+
let processed = 0;
|
|
311
|
+
while (queue.length > 0) {
|
|
312
|
+
const id = queue.shift();
|
|
313
|
+
processed++;
|
|
314
|
+
for (const succ of (successors.get(id) ?? [])) {
|
|
315
|
+
const newDeg = (inDegree.get(succ) ?? 1) - 1;
|
|
316
|
+
inDegree.set(succ, newDeg);
|
|
317
|
+
if (newDeg === 0)
|
|
318
|
+
queue.push(succ);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (processed < activities.length) {
|
|
322
|
+
const cycleNodes = activities.filter(a => (inDegree.get(a.id) ?? 0) > 0).map(a => a.id);
|
|
323
|
+
return { code: 'ACT-006', message: `Cycle detected in activity dependency graph involving: ${cycleNodes.join(', ')}` };
|
|
324
|
+
}
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Conformance test for the shipped worked example. Exercises the full
|
|
2
|
+
// validate → resolve → layout pipeline against the real co-located example
|
|
3
|
+
// files under `tests/fixtures/notation-corpus/activity-card/`, so the success signal of #134 is
|
|
4
|
+
// pinned and any drift in the example or the resolver is caught by CI.
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import { readFileSync, readdirSync } from 'node:fs';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import yaml from 'js-yaml';
|
|
10
|
+
import { validateActivityCard } from '../validate.js';
|
|
11
|
+
import { resolveActivityCard } from '../resolver.js';
|
|
12
|
+
import { layoutActivityCard } from '../layout.js';
|
|
13
|
+
const dir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../../tests/fixtures/notation-corpus/activity-card');
|
|
14
|
+
const load = (f) => yaml.load(readFileSync(path.join(dir, f), 'utf-8'));
|
|
15
|
+
/** Recursively load every `*.yaml` doc under a canon subtree (mirrors the
|
|
16
|
+
* extension's element/relation walk). */
|
|
17
|
+
function loadYamlTree(root) {
|
|
18
|
+
const out = [];
|
|
19
|
+
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
20
|
+
const full = path.join(root, entry.name);
|
|
21
|
+
if (entry.isDirectory())
|
|
22
|
+
out.push(...loadYamlTree(full));
|
|
23
|
+
else if (entry.name.endsWith('.yaml'))
|
|
24
|
+
out.push(yaml.load(readFileSync(full, 'utf-8')));
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
describe('activity-card worked example (eu-programme)', () => {
|
|
29
|
+
it('validates, resolves, and lays out the success-signal card', () => {
|
|
30
|
+
const card = load('eu-programme.activity-card.transitrix.yaml');
|
|
31
|
+
const elements = loadYamlTree(path.join(dir, 'canon', 'elements'));
|
|
32
|
+
const relations = loadYamlTree(path.join(dir, 'canon', 'relations'));
|
|
33
|
+
const v = validateActivityCard(card);
|
|
34
|
+
expect(v.valid, JSON.stringify(v.errors)).toBe(true);
|
|
35
|
+
const r = resolveActivityCard(card, { elements, relations });
|
|
36
|
+
expect(r.valid, JSON.stringify(r.errors)).toBe(true);
|
|
37
|
+
expect(r.warnings, JSON.stringify(r.warnings)).toHaveLength(0);
|
|
38
|
+
const c = r.resolved;
|
|
39
|
+
expect(c.project.name).toBe('EU MDR conformity programme');
|
|
40
|
+
expect(c.project.valid_from).toBe('2026-04-01');
|
|
41
|
+
expect(c.project.start_date).toBe('2026-04-15');
|
|
42
|
+
expect(c.project.end_date).toBe('2027-03-15');
|
|
43
|
+
expect(c.milestones.map((m) => m.id)).toEqual([
|
|
44
|
+
'MILESTONE-EU-CONFORMITY-CERT-1',
|
|
45
|
+
'MILESTONE-EU-MARKET-LAUNCH-1',
|
|
46
|
+
]);
|
|
47
|
+
expect(c.motivation.factors.map((f) => f.id)).toEqual(['FACTOR-EU-MDR-1']);
|
|
48
|
+
expect(c.motivation.goals.map((g) => g.id)).toEqual(['GOAL-EU-MARKET-1']);
|
|
49
|
+
expect(c.motivation.changes.map((x) => x.id)).toEqual(['CHANGE-EU-COMPLIANCE-1']);
|
|
50
|
+
expect(c.childActivities).toHaveLength(4);
|
|
51
|
+
const layout = layoutActivityCard(c);
|
|
52
|
+
expect(layout.bounds.height).toBeGreaterThan(0);
|
|
53
|
+
expect(layout.chainEdges).toContainEqual({ sourceId: 'FACTOR-EU-MDR-1', targetId: 'GOAL-EU-MARKET-1' });
|
|
54
|
+
expect(layout.chainEdges).toContainEqual({ sourceId: 'GOAL-EU-MARKET-1', targetId: 'CHANGE-EU-COMPLIANCE-1' });
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { layoutActivityCard, ARCHIMATE_CLASS } from '../layout.js';
|
|
3
|
+
const RESOLVED = {
|
|
4
|
+
cardId: 'ACTIVITY_CARD-EU-PROGRAMME-1',
|
|
5
|
+
cardDescription: 'Summary',
|
|
6
|
+
project: {
|
|
7
|
+
id: 'ACTIVITY-EU-PROGRAMME-1',
|
|
8
|
+
name: 'EU MDR conformity programme',
|
|
9
|
+
valid_from: '2026-04-01',
|
|
10
|
+
start_date: '2026-04-15',
|
|
11
|
+
end_date: '2027-03-15',
|
|
12
|
+
},
|
|
13
|
+
milestones: [
|
|
14
|
+
{ id: 'MILESTONE-CERT-1', name: 'Cert', date: '2027-01-31', deliversChanges: ['CHANGE-EU-COMPLIANCE-1'] },
|
|
15
|
+
],
|
|
16
|
+
motivation: {
|
|
17
|
+
factors: [{ id: 'FACTOR-EU-MDR-1', name: 'EU MDR regulation' }],
|
|
18
|
+
goals: [{ id: 'GOAL-EU-MARKET-1', name: 'Access EU market', factorIds: ['FACTOR-EU-MDR-1'] }],
|
|
19
|
+
changes: [{ id: 'CHANGE-EU-COMPLIANCE-1', name: 'Achieve MDR compliance', goalIds: ['GOAL-EU-MARKET-1'] }],
|
|
20
|
+
},
|
|
21
|
+
childActivities: [
|
|
22
|
+
{ id: 'ACTIVITY-EU-CHILD-1', name: 'Notified-body engagement', start_date: '2026-05-01', end_date: '2026-12-01', owner: 'UNIT-REGULATORY' },
|
|
23
|
+
],
|
|
24
|
+
};
|
|
25
|
+
describe('layoutActivityCard', () => {
|
|
26
|
+
it('produces all sections with positive geometry', () => {
|
|
27
|
+
const l = layoutActivityCard(RESOLVED);
|
|
28
|
+
expect(l.bounds.width).toBeGreaterThan(0);
|
|
29
|
+
expect(l.bounds.height).toBeGreaterThan(0);
|
|
30
|
+
expect(l.title.name).toBe('EU MDR conformity programme');
|
|
31
|
+
expect(l.dateFields).toHaveLength(3);
|
|
32
|
+
expect(l.dateFields[0]).toMatchObject({ label: 'Initiation', value: '2026-04-01' });
|
|
33
|
+
expect(l.milestones).toHaveLength(1);
|
|
34
|
+
expect(l.milestones[0].archimateClass).toBe(ARCHIMATE_CLASS.MILESTONE);
|
|
35
|
+
expect(l.childActivities[0].archimateClass).toBe(ARCHIMATE_CLASS.ACTIVITY);
|
|
36
|
+
});
|
|
37
|
+
it('builds F→G and G→C edges', () => {
|
|
38
|
+
const l = layoutActivityCard(RESOLVED);
|
|
39
|
+
expect(l.chainEdges).toContainEqual({ sourceId: 'FACTOR-EU-MDR-1', targetId: 'GOAL-EU-MARKET-1' });
|
|
40
|
+
expect(l.chainEdges).toContainEqual({ sourceId: 'GOAL-EU-MARKET-1', targetId: 'CHANGE-EU-COMPLIANCE-1' });
|
|
41
|
+
});
|
|
42
|
+
it('renders "—" for absent dates', () => {
|
|
43
|
+
const l = layoutActivityCard({ ...RESOLVED, project: { id: 'A', name: 'n' } });
|
|
44
|
+
expect(l.dateFields.map((d) => d.value)).toEqual(['—', '—', '—']);
|
|
45
|
+
});
|
|
46
|
+
it('handles an empty card without throwing', () => {
|
|
47
|
+
const l = layoutActivityCard({
|
|
48
|
+
cardId: 'ACTIVITY_CARD-X-1',
|
|
49
|
+
project: { id: 'ACTIVITY-X-1', name: 'Empty' },
|
|
50
|
+
milestones: [],
|
|
51
|
+
motivation: { factors: [], goals: [], changes: [] },
|
|
52
|
+
childActivities: [],
|
|
53
|
+
});
|
|
54
|
+
expect(l.milestones).toHaveLength(0);
|
|
55
|
+
expect(l.bounds.height).toBeGreaterThan(0);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { resolveActivityCard } from '../resolver.js';
|
|
3
|
+
const CARD = {
|
|
4
|
+
notation: 'activity-card',
|
|
5
|
+
spec_version: '0.1',
|
|
6
|
+
activity_card: {
|
|
7
|
+
id: 'ACTIVITY_CARD-EU-PROGRAMME-1',
|
|
8
|
+
project: 'ACTIVITY-EU-PROGRAMME-1',
|
|
9
|
+
description: 'Executive summary.',
|
|
10
|
+
milestones: [
|
|
11
|
+
{ id: 'MILESTONE-CERT-1', name: 'Cert', date: '2027-01-31', delivers_changes: ['CHANGE-EU-COMPLIANCE-1'] },
|
|
12
|
+
],
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
// ── Canon element store (one element per object) ─────────────────────────────
|
|
16
|
+
const PROJECT = {
|
|
17
|
+
notation: 'activity',
|
|
18
|
+
id: 'ACTIVITY-EU-PROGRAMME-1',
|
|
19
|
+
name: 'EU MDR conformity programme',
|
|
20
|
+
valid_from: '2026-04-01',
|
|
21
|
+
valid_to: null,
|
|
22
|
+
start_date: '2026-04-15',
|
|
23
|
+
end_date: '2027-03-15',
|
|
24
|
+
delivers_changes: ['CHANGE-EU-COMPLIANCE-1'],
|
|
25
|
+
};
|
|
26
|
+
const CHILD = {
|
|
27
|
+
notation: 'activity',
|
|
28
|
+
id: 'ACTIVITY-EU-CHILD-1',
|
|
29
|
+
name: 'Notified-body engagement',
|
|
30
|
+
parent: 'ACTIVITY-EU-PROGRAMME-1',
|
|
31
|
+
start_date: '2026-05-01',
|
|
32
|
+
end_date: '2026-12-01',
|
|
33
|
+
owner: 'ACTOR-REGULATORY-1',
|
|
34
|
+
};
|
|
35
|
+
const UNRELATED = {
|
|
36
|
+
notation: 'activity',
|
|
37
|
+
id: 'ACTIVITY-UNRELATED-1',
|
|
38
|
+
name: 'Other',
|
|
39
|
+
parent: 'ACTIVITY-SOMETHING-ELSE-1',
|
|
40
|
+
};
|
|
41
|
+
const FACTOR = { notation: 'factor', id: 'FACTOR-EU-MDR-1', name: 'EU MDR regulation' };
|
|
42
|
+
const GOAL = { notation: 'goal', id: 'GOAL-EU-MARKET-1', name: 'Access EU market', factors: ['FACTOR-EU-MDR-1'] };
|
|
43
|
+
const CHANGE = {
|
|
44
|
+
notation: 'change',
|
|
45
|
+
id: 'CHANGE-EU-COMPLIANCE-1',
|
|
46
|
+
name: 'Achieve MDR compliance',
|
|
47
|
+
goals: ['GOAL-EU-MARKET-1'],
|
|
48
|
+
};
|
|
49
|
+
// ── Canon relation store ─────────────────────────────────────────────────────
|
|
50
|
+
// The project's goal link is a first-class `activity_goal` relation, not an
|
|
51
|
+
// inline `goals:` field on the activity element.
|
|
52
|
+
const REL_GOAL = {
|
|
53
|
+
notation: 'relation',
|
|
54
|
+
id: 'REL-EU-PROGRAMME-GOAL-1',
|
|
55
|
+
type: 'activity_goal',
|
|
56
|
+
from: 'ACTIVITY-EU-PROGRAMME-1',
|
|
57
|
+
to: 'GOAL-EU-MARKET-1',
|
|
58
|
+
valid_from: '2026-04-01',
|
|
59
|
+
valid_to: null,
|
|
60
|
+
};
|
|
61
|
+
const elements = [PROJECT, CHILD, UNRELATED, FACTOR, GOAL, CHANGE];
|
|
62
|
+
const relations = [REL_GOAL];
|
|
63
|
+
const sources = { elements, relations };
|
|
64
|
+
describe('resolveActivityCard', () => {
|
|
65
|
+
it('resolves a full card from elements + relations', () => {
|
|
66
|
+
const r = resolveActivityCard(CARD, sources);
|
|
67
|
+
expect(r.valid, JSON.stringify(r.errors)).toBe(true);
|
|
68
|
+
const c = r.resolved;
|
|
69
|
+
expect(c.project.name).toBe('EU MDR conformity programme');
|
|
70
|
+
expect(c.project.valid_from).toBe('2026-04-01');
|
|
71
|
+
expect(c.project.start_date).toBe('2026-04-15');
|
|
72
|
+
expect(c.milestones).toHaveLength(1);
|
|
73
|
+
expect(c.motivation.factors.map((f) => f.id)).toEqual(['FACTOR-EU-MDR-1']);
|
|
74
|
+
expect(c.motivation.goals.map((g) => g.id)).toEqual(['GOAL-EU-MARKET-1']);
|
|
75
|
+
expect(c.motivation.changes.map((ch) => ch.id)).toEqual(['CHANGE-EU-COMPLIANCE-1']);
|
|
76
|
+
// only the activity whose parent = the project
|
|
77
|
+
expect(c.childActivities.map((a) => a.id)).toEqual(['ACTIVITY-EU-CHILD-1']);
|
|
78
|
+
});
|
|
79
|
+
it('falls back to the inline goals field when no activity_goal relation exists', () => {
|
|
80
|
+
const projectWithInline = { ...PROJECT, goals: ['GOAL-EU-MARKET-1'] };
|
|
81
|
+
const r = resolveActivityCard(CARD, {
|
|
82
|
+
elements: [projectWithInline, CHILD, FACTOR, GOAL, CHANGE],
|
|
83
|
+
relations: [],
|
|
84
|
+
});
|
|
85
|
+
expect(r.valid).toBe(true);
|
|
86
|
+
expect(r.resolved.motivation.goals.map((g) => g.id)).toEqual(['GOAL-EU-MARKET-1']);
|
|
87
|
+
});
|
|
88
|
+
it('PC-001 when the project activity element is absent', () => {
|
|
89
|
+
const r = resolveActivityCard(CARD, { elements: [FACTOR, GOAL, CHANGE], relations });
|
|
90
|
+
expect(r.valid).toBe(false);
|
|
91
|
+
expect(r.errors.some((e) => e.code === 'PC-001')).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
it('PC-002 when the activity carries an explicit non-project type', () => {
|
|
94
|
+
const r = resolveActivityCard(CARD, {
|
|
95
|
+
elements: [{ ...PROJECT, activity_type: 'Task' }, CHILD, FACTOR, GOAL, CHANGE],
|
|
96
|
+
relations,
|
|
97
|
+
});
|
|
98
|
+
expect(r.errors.some((e) => e.code === 'PC-002')).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
it('PC-003 when a milestone change is not in the project changes', () => {
|
|
101
|
+
const card = {
|
|
102
|
+
...CARD,
|
|
103
|
+
activity_card: {
|
|
104
|
+
...CARD.activity_card,
|
|
105
|
+
milestones: [{ id: 'MILESTONE-X-1', name: 'x', date: '2027-01-01', delivers_changes: ['CHANGE-NOT-THERE-1'] }],
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
const r = resolveActivityCard(card, sources);
|
|
109
|
+
expect(r.errors.some((e) => e.code === 'PC-003')).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
it('PC-004 warns when a milestone falls outside the lifecycle window', () => {
|
|
112
|
+
const r = resolveActivityCard(CARD, {
|
|
113
|
+
elements: [{ ...PROJECT, valid_to: '2026-12-31' }, CHILD, FACTOR, GOAL, CHANGE],
|
|
114
|
+
relations,
|
|
115
|
+
});
|
|
116
|
+
expect(r.valid).toBe(true);
|
|
117
|
+
expect(r.warnings.some((w) => w.code === 'PC-004')).toBe(true);
|
|
118
|
+
});
|
|
119
|
+
it('LIFECYCLE-001 when valid_from is missing', () => {
|
|
120
|
+
const r = resolveActivityCard(CARD, {
|
|
121
|
+
elements: [{ ...PROJECT, valid_from: undefined }, CHILD, FACTOR, GOAL, CHANGE],
|
|
122
|
+
relations,
|
|
123
|
+
});
|
|
124
|
+
expect(r.errors.some((e) => e.code === 'LIFECYCLE-001')).toBe(true);
|
|
125
|
+
});
|
|
126
|
+
it('LIFECYCLE-003 when valid_to precedes valid_from', () => {
|
|
127
|
+
const r = resolveActivityCard(CARD, {
|
|
128
|
+
elements: [{ ...PROJECT, valid_to: '2025-01-01' }, CHILD, FACTOR, GOAL, CHANGE],
|
|
129
|
+
relations,
|
|
130
|
+
});
|
|
131
|
+
expect(r.errors.some((e) => e.code === 'LIFECYCLE-003')).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
it('warns and omits when a change element is not found in canon', () => {
|
|
134
|
+
const r = resolveActivityCard(CARD, { elements: [PROJECT, CHILD, FACTOR, GOAL], relations });
|
|
135
|
+
expect(r.valid).toBe(true);
|
|
136
|
+
expect(r.resolved.motivation.changes).toHaveLength(0);
|
|
137
|
+
expect(r.warnings.some((w) => w.code === 'PC-001')).toBe(true);
|
|
138
|
+
});
|
|
139
|
+
it('does not crash on malformed element/relation docs', () => {
|
|
140
|
+
const r = resolveActivityCard(CARD, {
|
|
141
|
+
elements: [null, 'x', PROJECT, 42, { notation: 'goal', id: null }],
|
|
142
|
+
relations: [null, 'x', { notation: 'relation' }],
|
|
143
|
+
});
|
|
144
|
+
expect(r.valid).toBe(true);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|