@owlmeans/server-planning 0.1.18-rc.9 → 0.1.18
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/README.md +92 -19
- package/agent-meta/manifest.json +105 -4
- package/agent-meta/skills/server-planning/SKILL.md +89 -138
- package/agent-meta/skills/server-planning/parts/auxiliary.md +54 -0
- package/agent-meta/skills/server-planning/parts/executor.md +82 -0
- package/agent-meta/skills/server-planning/parts/schemas.md +26 -0
- package/agent-meta/skills/server-planning/parts/serving.md +88 -0
- package/agent-meta/skills/server-planning/parts/stores.md +91 -0
- package/build/actions/apply-schema-request.d.ts +8 -0
- package/build/actions/apply-schema-request.d.ts.map +1 -0
- package/build/actions/apply-schema-request.js +33 -0
- package/build/actions/apply-schema-request.js.map +1 -0
- package/build/actions/commit.d.ts +3 -3
- package/build/actions/commit.d.ts.map +1 -1
- package/build/actions/commit.js +5 -4
- package/build/actions/commit.js.map +1 -1
- package/build/actions/define-schemas.d.ts +13 -0
- package/build/actions/define-schemas.d.ts.map +1 -0
- package/build/actions/define-schemas.js +28 -0
- package/build/actions/define-schemas.js.map +1 -0
- package/build/actions/execute.d.ts +11 -10
- package/build/actions/execute.d.ts.map +1 -1
- package/build/actions/execute.js +17 -27
- package/build/actions/execute.js.map +1 -1
- package/build/actions/execution/types.d.ts +34 -0
- package/build/actions/execution/types.d.ts.map +1 -0
- package/build/actions/execution/types.js +2 -0
- package/build/actions/execution/types.js.map +1 -0
- package/build/actions/execution.d.ts +4 -0
- package/build/actions/execution.d.ts.map +1 -0
- package/build/actions/execution.js +88 -0
- package/build/actions/execution.js.map +1 -0
- package/build/actions/get-card.d.ts +6 -0
- package/build/actions/get-card.d.ts.map +1 -0
- package/build/actions/get-card.js +9 -0
- package/build/actions/get-card.js.map +1 -0
- package/build/actions/get-specification.d.ts +6 -0
- package/build/actions/get-specification.d.ts.map +1 -0
- package/build/actions/get-specification.js +9 -0
- package/build/actions/get-specification.js.map +1 -0
- package/build/actions/index.d.ts +12 -3
- package/build/actions/index.d.ts.map +1 -1
- package/build/actions/index.js +11 -3
- package/build/actions/index.js.map +1 -1
- package/build/actions/links.d.ts +3 -3
- package/build/actions/links.d.ts.map +1 -1
- package/build/actions/links.js +6 -5
- package/build/actions/links.js.map +1 -1
- package/build/actions/list-card-specifications.d.ts +6 -0
- package/build/actions/list-card-specifications.d.ts.map +1 -0
- package/build/actions/list-card-specifications.js +11 -0
- package/build/actions/list-card-specifications.js.map +1 -0
- package/build/actions/list-card-transitions.d.ts +6 -0
- package/build/actions/list-card-transitions.d.ts.map +1 -0
- package/build/actions/list-card-transitions.js +11 -0
- package/build/actions/list-card-transitions.js.map +1 -0
- package/build/actions/list-cards.d.ts +6 -0
- package/build/actions/list-cards.d.ts.map +1 -0
- package/build/actions/list-cards.js +10 -0
- package/build/actions/list-cards.js.map +1 -0
- package/build/actions/list-schemas.d.ts +13 -0
- package/build/actions/list-schemas.d.ts.map +1 -0
- package/build/actions/list-schemas.js +20 -0
- package/build/actions/list-schemas.js.map +1 -0
- package/build/actions/list-specification-revisions.d.ts +6 -0
- package/build/actions/list-specification-revisions.d.ts.map +1 -0
- package/build/actions/list-specification-revisions.js +10 -0
- package/build/actions/list-specification-revisions.js.map +1 -0
- package/build/actions/resources.d.ts +5 -0
- package/build/actions/resources.d.ts.map +1 -0
- package/build/actions/resources.js +111 -0
- package/build/actions/resources.js.map +1 -0
- package/build/actions/summarize-cards.d.ts +6 -0
- package/build/actions/summarize-cards.d.ts.map +1 -0
- package/build/actions/summarize-cards.js +11 -0
- package/build/actions/summarize-cards.js.map +1 -0
- package/build/actions/transitions.d.ts +3 -3
- package/build/actions/transitions.d.ts.map +1 -1
- package/build/actions/transitions.js +4 -3
- package/build/actions/transitions.js.map +1 -1
- package/build/actions/types.d.ts +6 -0
- package/build/actions/types.d.ts.map +1 -0
- package/build/actions/types.js +2 -0
- package/build/actions/types.js.map +1 -0
- package/build/actions/watch.d.ts +2 -2
- package/build/actions/watch.d.ts.map +1 -1
- package/build/actions/watch.js +8 -6
- package/build/actions/watch.js.map +1 -1
- package/build/conformance/assert/types.d.ts +19 -0
- package/build/conformance/assert/types.d.ts.map +1 -0
- package/build/conformance/assert/types.js +2 -0
- package/build/conformance/assert/types.js.map +1 -0
- package/build/conformance/assert.d.ts +15 -0
- package/build/conformance/assert.d.ts.map +1 -0
- package/build/conformance/assert.js +52 -0
- package/build/conformance/assert.js.map +1 -0
- package/build/conformance/cases.d.ts +7 -0
- package/build/conformance/cases.d.ts.map +1 -0
- package/build/conformance/cases.js +544 -0
- package/build/conformance/cases.js.map +1 -0
- package/build/conformance/clock.d.ts +6 -0
- package/build/conformance/clock.d.ts.map +1 -0
- package/build/conformance/clock.js +12 -0
- package/build/conformance/clock.js.map +1 -0
- package/build/conformance/consts.d.ts +30 -0
- package/build/conformance/consts.d.ts.map +1 -0
- package/build/conformance/consts.js +159 -0
- package/build/conformance/consts.js.map +1 -0
- package/build/conformance/errors.d.ts +5 -0
- package/build/conformance/errors.d.ts.map +1 -0
- package/build/conformance/errors.js +8 -0
- package/build/conformance/errors.js.map +1 -0
- package/build/conformance/fixtures/types.d.ts +13 -0
- package/build/conformance/fixtures/types.d.ts.map +1 -0
- package/build/conformance/fixtures/types.js +2 -0
- package/build/conformance/fixtures/types.js.map +1 -0
- package/build/conformance/fixtures.d.ts +14 -0
- package/build/conformance/fixtures.d.ts.map +1 -0
- package/build/conformance/fixtures.js +26 -0
- package/build/conformance/fixtures.js.map +1 -0
- package/build/conformance/index.d.ts +15 -0
- package/build/conformance/index.d.ts.map +1 -0
- package/build/conformance/index.js +12 -0
- package/build/conformance/index.js.map +1 -0
- package/build/conformance/resources.d.ts +4 -0
- package/build/conformance/resources.d.ts.map +1 -0
- package/build/conformance/resources.js +228 -0
- package/build/conformance/resources.js.map +1 -0
- package/build/conformance/types.d.ts +18 -0
- package/build/conformance/types.d.ts.map +1 -0
- package/build/conformance/types.js +2 -0
- package/build/conformance/types.js.map +1 -0
- package/build/consts.d.ts +5 -0
- package/build/consts.d.ts.map +1 -1
- package/build/consts.js +5 -0
- package/build/consts.js.map +1 -1
- package/build/definitions.d.ts +18 -0
- package/build/definitions.d.ts.map +1 -0
- package/build/definitions.js +225 -0
- package/build/definitions.js.map +1 -0
- package/build/executor/changes/types.d.ts +36 -0
- package/build/executor/changes/types.d.ts.map +1 -0
- package/build/executor/changes/types.js +2 -0
- package/build/executor/changes/types.js.map +1 -0
- package/build/executor/changes.d.ts +3 -30
- package/build/executor/changes.d.ts.map +1 -1
- package/build/executor/changes.js +42 -51
- package/build/executor/changes.js.map +1 -1
- package/build/executor/code/types.d.ts +17 -0
- package/build/executor/code/types.d.ts.map +1 -0
- package/build/executor/code/types.js +2 -0
- package/build/executor/code/types.js.map +1 -0
- package/build/executor/code.d.ts +3 -14
- package/build/executor/code.d.ts.map +1 -1
- package/build/executor/code.js +50 -54
- package/build/executor/code.js.map +1 -1
- package/build/executor/consts.local.d.ts +5 -0
- package/build/executor/consts.local.d.ts.map +1 -0
- package/build/executor/consts.local.js +6 -0
- package/build/executor/consts.local.js.map +1 -0
- package/build/executor/creator/types.d.ts +33 -0
- package/build/executor/creator/types.d.ts.map +1 -0
- package/build/executor/creator/types.js +2 -0
- package/build/executor/creator/types.js.map +1 -0
- package/build/executor/creator.d.ts +11 -0
- package/build/executor/creator.d.ts.map +1 -0
- package/build/executor/creator.js +32 -0
- package/build/executor/creator.js.map +1 -0
- package/build/executor/resolve/types.d.ts +30 -0
- package/build/executor/resolve/types.d.ts.map +1 -0
- package/build/executor/resolve/types.js +2 -0
- package/build/executor/resolve/types.js.map +1 -0
- package/build/executor/resolve.d.ts +5 -34
- package/build/executor/resolve.d.ts.map +1 -1
- package/build/executor/resolve.js +169 -110
- package/build/executor/resolve.js.map +1 -1
- package/build/executor/types.d.ts +23 -0
- package/build/executor/types.d.ts.map +1 -0
- package/build/executor/types.js +2 -0
- package/build/executor/types.js.map +1 -0
- package/build/executor/validate.d.ts +6 -5
- package/build/executor/validate.d.ts.map +1 -1
- package/build/executor/validate.js +38 -27
- package/build/executor/validate.js.map +1 -1
- package/build/executor.d.ts +11 -7
- package/build/executor.d.ts.map +1 -1
- package/build/executor.js +27 -21
- package/build/executor.js.map +1 -1
- package/build/facade.d.ts +20 -2
- package/build/facade.d.ts.map +1 -1
- package/build/facade.js +144 -22
- package/build/facade.js.map +1 -1
- package/build/helper.d.ts +5 -9
- package/build/helper.d.ts.map +1 -1
- package/build/helper.js +12 -11
- package/build/helper.js.map +1 -1
- package/build/hierarchy/types.d.ts +20 -0
- package/build/hierarchy/types.d.ts.map +1 -0
- package/build/hierarchy/types.js +2 -0
- package/build/hierarchy/types.js.map +1 -0
- package/build/hierarchy.d.ts +5 -0
- package/build/hierarchy.d.ts.map +1 -0
- package/build/hierarchy.js +133 -0
- package/build/hierarchy.js.map +1 -0
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/build/invariants/types.d.ts +6 -0
- package/build/invariants/types.d.ts.map +1 -0
- package/build/invariants/types.js +2 -0
- package/build/invariants/types.js.map +1 -0
- package/build/invariants.d.ts +5 -0
- package/build/invariants.d.ts.map +1 -0
- package/build/invariants.js +126 -0
- package/build/invariants.js.map +1 -0
- package/build/projection.d.ts +4 -10
- package/build/projection.d.ts.map +1 -1
- package/build/projection.js +7 -5
- package/build/projection.js.map +1 -1
- package/build/queue/consts.d.ts +5 -0
- package/build/queue/consts.d.ts.map +1 -0
- package/build/queue/consts.js +5 -0
- package/build/queue/consts.js.map +1 -0
- package/build/queue/declare.d.ts +5 -0
- package/build/queue/declare.d.ts.map +1 -0
- package/build/queue/declare.js +5 -0
- package/build/queue/declare.js.map +1 -0
- package/build/queue/index.d.ts +3 -18
- package/build/queue/index.d.ts.map +1 -1
- package/build/queue/index.js +2 -7
- package/build/queue/index.js.map +1 -1
- package/build/queue/types.d.ts +13 -0
- package/build/queue/types.d.ts.map +1 -0
- package/build/queue/types.js +2 -0
- package/build/queue/types.js.map +1 -0
- package/build/registry.d.ts +5 -22
- package/build/registry.d.ts.map +1 -1
- package/build/registry.js +16 -10
- package/build/registry.js.map +1 -1
- package/build/resources.d.ts +5 -0
- package/build/resources.d.ts.map +1 -0
- package/build/resources.js +287 -0
- package/build/resources.js.map +1 -0
- package/build/service.d.ts +3 -16
- package/build/service.d.ts.map +1 -1
- package/build/service.js +12 -0
- package/build/service.js.map +1 -1
- package/build/store/commits.d.ts.map +1 -1
- package/build/store/commits.js +3 -1
- package/build/store/commits.js.map +1 -1
- package/build/store/composite.d.ts +1 -0
- package/build/store/composite.d.ts.map +1 -1
- package/build/store/composite.js +7 -2
- package/build/store/composite.js.map +1 -1
- package/build/store/consts.local.d.ts +2 -0
- package/build/store/consts.local.d.ts.map +1 -0
- package/build/store/consts.local.js +2 -0
- package/build/store/consts.local.js.map +1 -0
- package/build/store/fold/types.d.ts +43 -0
- package/build/store/fold/types.d.ts.map +1 -0
- package/build/store/fold/types.js +2 -0
- package/build/store/fold/types.js.map +1 -0
- package/build/store/fold.d.ts +8 -30
- package/build/store/fold.d.ts.map +1 -1
- package/build/store/fold.js +190 -198
- package/build/store/fold.js.map +1 -1
- package/build/store/index.d.ts +1 -0
- package/build/store/index.d.ts.map +1 -1
- package/build/store/index.js.map +1 -1
- package/build/store/memory.d.ts +12 -0
- package/build/store/memory.d.ts.map +1 -1
- package/build/store/memory.js +0 -0
- package/build/store/memory.js.map +1 -1
- package/build/store/types.d.ts +15 -1
- package/build/store/types.d.ts.map +1 -1
- package/build/store/types.local.d.ts +5 -0
- package/build/store/types.local.d.ts.map +1 -0
- package/build/store/types.local.js +2 -0
- package/build/store/types.local.js.map +1 -0
- package/build/types.d.ts +126 -2
- package/build/types.d.ts.map +1 -1
- package/build/types.local.d.ts +7 -0
- package/build/types.local.d.ts.map +1 -0
- package/build/types.local.js +2 -0
- package/build/types.local.js.map +1 -0
- package/build/utils/access/types.d.ts +30 -0
- package/build/utils/access/types.d.ts.map +1 -0
- package/build/utils/access/types.js +2 -0
- package/build/utils/access/types.js.map +1 -0
- package/build/utils/access.d.ts +4 -0
- package/build/utils/access.d.ts.map +1 -0
- package/build/utils/access.js +28 -0
- package/build/utils/access.js.map +1 -0
- package/build/utils/guard/types.d.ts +23 -0
- package/build/utils/guard/types.d.ts.map +1 -0
- package/build/utils/guard/types.js +2 -0
- package/build/utils/guard/types.js.map +1 -0
- package/build/utils/guard.d.ts +7 -10
- package/build/utils/guard.d.ts.map +1 -1
- package/build/utils/guard.js +36 -32
- package/build/utils/guard.js.map +1 -1
- package/build/utils/handler/types.d.ts +26 -0
- package/build/utils/handler/types.d.ts.map +1 -0
- package/build/utils/handler/types.js +2 -0
- package/build/utils/handler/types.js.map +1 -0
- package/build/utils/handler.d.ts +6 -0
- package/build/utils/handler.d.ts.map +1 -0
- package/build/utils/handler.js +21 -0
- package/build/utils/handler.js.map +1 -0
- package/build/utils/index.d.ts +8 -0
- package/build/utils/index.d.ts.map +1 -1
- package/build/utils/index.js +3 -0
- package/build/utils/index.js.map +1 -1
- package/build/utils/request/types.d.ts +23 -0
- package/build/utils/request/types.d.ts.map +1 -0
- package/build/utils/request/types.js +2 -0
- package/build/utils/request/types.js.map +1 -0
- package/build/utils/request.d.ts +4 -0
- package/build/utils/request.d.ts.map +1 -0
- package/build/utils/request.js +27 -0
- package/build/utils/request.js.map +1 -0
- package/build/utils/scope.d.ts +17 -13
- package/build/utils/scope.d.ts.map +1 -1
- package/build/utils/scope.js +21 -35
- package/build/utils/scope.js.map +1 -1
- package/build/utils/types.d.ts +8 -0
- package/build/utils/types.d.ts.map +1 -0
- package/build/utils/types.js +2 -0
- package/build/utils/types.js.map +1 -0
- package/package.json +30 -22
- package/src/actions/apply-schema-request.ts +36 -0
- package/src/actions/commit.ts +7 -5
- package/src/actions/define-schemas.ts +34 -0
- package/src/actions/execute.ts +32 -34
- package/src/actions/execution/types.ts +34 -0
- package/src/actions/execution.ts +100 -0
- package/src/actions/get-card.ts +14 -0
- package/src/actions/get-specification.ts +14 -0
- package/src/actions/index.ts +13 -3
- package/src/actions/links.ts +8 -6
- package/src/actions/list-card-specifications.ts +15 -0
- package/src/actions/list-card-transitions.ts +15 -0
- package/src/actions/list-cards.ts +14 -0
- package/src/actions/list-schemas.ts +29 -0
- package/src/actions/list-specification-revisions.ts +15 -0
- package/src/actions/resources.ts +79 -0
- package/src/actions/summarize-cards.ts +15 -0
- package/src/actions/transitions.ts +6 -4
- package/src/actions/types.ts +5 -0
- package/src/actions/watch.ts +11 -7
- package/src/conformance/assert/types.ts +16 -0
- package/src/conformance/assert.ts +67 -0
- package/src/conformance/cases.ts +631 -0
- package/src/conformance/clock.ts +11 -0
- package/src/conformance/consts.ts +165 -0
- package/src/conformance/errors.ts +7 -0
- package/src/conformance/fixtures/types.ts +11 -0
- package/src/conformance/fixtures.ts +46 -0
- package/src/conformance/index.ts +14 -0
- package/src/conformance/resources.ts +227 -0
- package/src/conformance/types.ts +20 -0
- package/src/consts.ts +6 -0
- package/src/definitions.ts +264 -0
- package/src/executor/changes/types.ts +38 -0
- package/src/executor/changes.ts +49 -68
- package/src/executor/code/types.ts +23 -0
- package/src/executor/code.ts +62 -69
- package/src/executor/consts.local.ts +8 -0
- package/src/executor/creator/types.ts +33 -0
- package/src/executor/creator.ts +43 -0
- package/src/executor/resolve/types.ts +30 -0
- package/src/executor/resolve.ts +181 -139
- package/src/executor/types.ts +23 -0
- package/src/executor/validate.ts +40 -38
- package/src/executor.ts +33 -27
- package/src/facade.ts +132 -29
- package/src/helper.ts +12 -11
- package/src/hierarchy/types.ts +20 -0
- package/src/hierarchy.ts +113 -0
- package/src/index.ts +1 -0
- package/src/invariants/types.ts +6 -0
- package/src/invariants.ts +106 -0
- package/src/projection.ts +12 -18
- package/src/queue/consts.ts +5 -0
- package/src/queue/declare.ts +7 -0
- package/src/queue/index.ts +3 -25
- package/src/queue/types.ts +14 -0
- package/src/registry.ts +18 -36
- package/src/resources.ts +256 -0
- package/src/service.ts +16 -20
- package/src/store/commits.ts +5 -4
- package/src/store/composite.ts +7 -2
- package/src/store/consts.local.ts +1 -0
- package/src/store/fold/types.ts +44 -0
- package/src/store/fold.ts +214 -212
- package/src/store/index.ts +1 -0
- package/src/store/memory.ts +0 -0
- package/src/store/types.local.ts +3 -0
- package/src/store/types.ts +16 -1
- package/src/types.local.ts +7 -0
- package/src/types.ts +139 -6
- package/src/utils/access/types.ts +30 -0
- package/src/utils/access.ts +33 -0
- package/src/utils/guard/types.ts +21 -0
- package/src/utils/guard.ts +44 -31
- package/src/utils/handler/types.ts +28 -0
- package/src/utils/handler.ts +35 -0
- package/src/utils/index.ts +9 -0
- package/src/utils/request/types.ts +23 -0
- package/src/utils/request.ts +40 -0
- package/src/utils/scope.ts +39 -42
- package/src/utils/types.ts +8 -0
- package/tests/access.spec.ts +160 -0
- package/tests/app.spec.ts +112 -0
- package/tests/conformance.spec.ts +47 -0
- package/tests/create-example.spec.ts +202 -0
- package/tests/creator.spec.ts +257 -0
- package/tests/entrypoints.spec.ts +5 -0
- package/tests/memory-store.spec.ts +52 -0
- package/tests/narrowing.spec.ts +94 -0
- package/tests/plugins.spec.ts +4 -3
- package/tests/writes.spec.ts +140 -0
- package/build/actions/cards.d.ts +0 -16
- package/build/actions/cards.d.ts.map +0 -1
- package/build/actions/cards.js +0 -32
- package/build/actions/cards.js.map +0 -1
- package/build/actions/schemas.d.ts +0 -6
- package/build/actions/schemas.d.ts.map +0 -1
- package/build/actions/schemas.js +0 -9
- package/build/actions/schemas.js.map +0 -1
- package/build/actions/specs.d.ts +0 -10
- package/build/actions/specs.d.ts.map +0 -1
- package/build/actions/specs.js +0 -14
- package/build/actions/specs.js.map +0 -1
- package/src/actions/cards.ts +0 -52
- package/src/actions/schemas.ts +0 -15
- package/src/actions/specs.ts +0 -23
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { PlanningError, TransitionAction, type PlanningScope, type TransitionExecution } from '@owlmeans/planning'
|
|
2
|
+
import { CREATOR } from './consts.local.js'
|
|
3
|
+
import type { CreatorHelper } from './creator/types.js'
|
|
4
|
+
|
|
5
|
+
const named = (value?: string | null): value is string => value != null && value !== ''
|
|
6
|
+
|
|
7
|
+
export const createCreatorHelper = (): CreatorHelper => {
|
|
8
|
+
const creatorOf = (scope: PlanningScope): string | undefined =>
|
|
9
|
+
[scope.profileId, scope.userId, scope.actor?.profileId, scope.actor?.userId].find(named)
|
|
10
|
+
|
|
11
|
+
const withCreator = (exec: TransitionExecution, scope: PlanningScope): TransitionExecution => {
|
|
12
|
+
if (exec.action !== TransitionAction.Create || exec.card == null || typeof exec.card !== 'object') {
|
|
13
|
+
return exec
|
|
14
|
+
}
|
|
15
|
+
const creator = named(exec.card.createdBy) ? exec.card.createdBy : creatorOf(scope)
|
|
16
|
+
return { ...exec, card: { ...exec.card,
|
|
17
|
+
...(creator != null ? { createdBy: creator } : {}),
|
|
18
|
+
...(exec.card.reporter == null && scope.assigneeId != null ? { reporter: scope.assigneeId } : {}),
|
|
19
|
+
...(exec.card.assignee == null && scope.defaultAssigneeId != null ? { assignee: scope.defaultAssigneeId } : {}),
|
|
20
|
+
} }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const assertCreatorFixed = (exec: TransitionExecution): void => {
|
|
24
|
+
if ((exec.changes as Record<string, unknown> | undefined)?.[CREATOR] !== undefined
|
|
25
|
+
|| (exec.unset ?? []).some(path => path.split('.')[0] === CREATOR)) {
|
|
26
|
+
throw new PlanningError(`immutable:${CREATOR}`)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return { creatorOf, withCreator, assertCreatorFixed }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const creatorHelper = createCreatorHelper()
|
|
34
|
+
|
|
35
|
+
/** @deprecated compat:factory-refactor — use `creatorHelper.creatorOf(…)` */
|
|
36
|
+
export const creatorOf = (scope: PlanningScope): string | undefined => creatorHelper.creatorOf(scope)
|
|
37
|
+
|
|
38
|
+
/** @deprecated compat:factory-refactor — use `creatorHelper.withCreator(…)` */
|
|
39
|
+
export const withCreator = (exec: TransitionExecution, scope: PlanningScope): TransitionExecution =>
|
|
40
|
+
creatorHelper.withCreator(exec, scope)
|
|
41
|
+
|
|
42
|
+
/** @deprecated compat:factory-refactor — use `creatorHelper.assertCreatorFixed(…)` */
|
|
43
|
+
export const assertCreatorFixed = (exec: TransitionExecution): void => creatorHelper.assertCreatorFixed(exec)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { PlanningFacade, PlanningSchemaRegistry, TransitionExecution, Workcard, WorkcardKind } from '@owlmeans/planning'
|
|
2
|
+
import type { Resolved } from '../types.js'
|
|
3
|
+
|
|
4
|
+
/** Steps 1 and 4 of the write path, over one service runtime. */
|
|
5
|
+
export interface ResolveUtils {
|
|
6
|
+
/**
|
|
7
|
+
* Step 1: a deep copy with trimmed text, `parents ∋ parent` on a draft, and no duplicate `unset`
|
|
8
|
+
* paths. The caller's object is never touched.
|
|
9
|
+
*/
|
|
10
|
+
normalizeExecution: (input: TransitionExecution) => TransitionExecution
|
|
11
|
+
/**
|
|
12
|
+
* The parent type's rule for a child of `kind`/`type`: a project lists the `cardTypes` (and
|
|
13
|
+
* `projectTypes`) it admits, and one declaring `scopedCardTypes` also admits a card type its layer
|
|
14
|
+
* (`view`, the parent project's resolved schemas) defines as data. Project types are code's alone.
|
|
15
|
+
*
|
|
16
|
+
* @throws {CardTypeNotAllowed}
|
|
17
|
+
*/
|
|
18
|
+
assertChildAllowed: (parent: Workcard, kind: WorkcardKind, type: string, view?: PlanningSchemaRegistry) => void
|
|
19
|
+
/** The layer a parent project admits data-defined children through — `undefined` without a schema port. */
|
|
20
|
+
childViewOf: (entityId: string, parent: Workcard) => Promise<PlanningSchemaRegistry | undefined>
|
|
21
|
+
/**
|
|
22
|
+
* Step 4: the card, its type and flow, its parent and — for a specification — its slot.
|
|
23
|
+
*
|
|
24
|
+
* @throws {WorkcardNotFound | PlanningScopeMismatch | UnknownWorkcardType | UnknownStatusFlow}
|
|
25
|
+
* @throws {ParentNotFound | CardTypeNotAllowed | PlanningError}
|
|
26
|
+
*/
|
|
27
|
+
resolveExecution: (facade: PlanningFacade, exec: TransitionExecution) => Promise<Resolved>
|
|
28
|
+
/** The project a transition is filed under: a project's own id, else its parent's project. */
|
|
29
|
+
projectFor: (resolved: Resolved, exec: TransitionExecution, cardId: string) => string | undefined
|
|
30
|
+
}
|
package/src/executor/resolve.ts
CHANGED
|
@@ -1,33 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from '
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
/** The primary parent, when it resolves. */
|
|
20
|
-
parent?: Workcard
|
|
21
|
-
/** The slot a specification belongs to. */
|
|
22
|
-
slot?: SpecificationSlot
|
|
23
|
-
/** The store the type is written to. */
|
|
24
|
-
store: PlanningStore
|
|
1
|
+
import { cardHelper, CardTypeNotAllowed, ParentNotFound, PlanningError, PlanningSchemaKind, PlanningScopeMismatch, SchemaOrigin, specificationHelper, statusHelper, TransitionAction, UnknownStatusFlow, UnknownWorkcardType, WorkcardKind, WorkcardNotFound, type AnyTypeSchema, type PlanningFacade, type PlanningSchemaRegistry, type ProjectTypeSchema, type ScopedSchemaRegistry, type Specification, type TransitionExecution, type Workcard, type WorkcardDraft } from '@owlmeans/planning'
|
|
2
|
+
import { makePlanningHierarchy } from '../hierarchy.js'
|
|
3
|
+
import { memoHelper } from '@owlmeans/context'
|
|
4
|
+
import type { PlanningRuntime } from '../types.js'
|
|
5
|
+
import { TRIMMED } from './consts.local.js'
|
|
6
|
+
import type { Resolved } from './types.js'
|
|
7
|
+
import type { ResolveUtils } from './resolve/types.js'
|
|
8
|
+
|
|
9
|
+
const isScoped = (schemas: PlanningSchemaRegistry): schemas is ScopedSchemaRegistry =>
|
|
10
|
+
typeof (schemas as Partial<ScopedSchemaRegistry>).originOf === 'function'
|
|
11
|
+
|
|
12
|
+
/** A type the layer resolves from a data-defined record rather than from code. */
|
|
13
|
+
const dataDefined = (schemas: PlanningSchemaRegistry | undefined, type: string): boolean => {
|
|
14
|
+
if (schemas == null || !isScoped(schemas) || !schemas.has(type)) {
|
|
15
|
+
return false
|
|
16
|
+
}
|
|
17
|
+
const origin = schemas.originOf(PlanningSchemaKind.Type, type)
|
|
18
|
+
return origin === SchemaOrigin.Entity || origin === SchemaOrigin.Project
|
|
25
19
|
}
|
|
26
20
|
|
|
27
21
|
const trimmed = (value: unknown): unknown => typeof value === 'string' ? value.trim() : value
|
|
28
22
|
|
|
29
|
-
const TRIMMED = ['title', 'description', 'code'] as const
|
|
30
|
-
|
|
31
23
|
const trim = <T extends object>(record: T | undefined): T | undefined => {
|
|
32
24
|
if (record == null) {
|
|
33
25
|
return record
|
|
@@ -41,134 +33,184 @@ const trim = <T extends object>(record: T | undefined): T | undefined => {
|
|
|
41
33
|
return copy as T
|
|
42
34
|
}
|
|
43
35
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
exec.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
36
|
+
export const makeResolveUtils = (runtime: PlanningRuntime): ResolveUtils => {
|
|
37
|
+
const hierarchy = makePlanningHierarchy(runtime)
|
|
38
|
+
const normalizeExecution = (input: TransitionExecution): TransitionExecution => {
|
|
39
|
+
const exec = structuredClone(input) as TransitionExecution
|
|
40
|
+
if (exec.card != null && typeof exec.card === 'object') {
|
|
41
|
+
const draft = trim(exec.card) as WorkcardDraft
|
|
42
|
+
const parents = cardHelper.normalizeParents(draft.parent, draft.parents)
|
|
43
|
+
exec.card = { ...draft, ...(parents.length > 0 ? { parents, parent: draft.parent ?? parents[0] } : {}) }
|
|
44
|
+
}
|
|
45
|
+
if (exec.changes != null) {
|
|
46
|
+
exec.changes = trim(exec.changes)
|
|
47
|
+
}
|
|
48
|
+
if (exec.unset != null) {
|
|
49
|
+
exec.unset = [...new Set(exec.unset)]
|
|
50
|
+
}
|
|
51
|
+
return exec
|
|
60
52
|
}
|
|
61
|
-
return exec
|
|
62
|
-
}
|
|
63
53
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
54
|
+
const assertChildAllowed = (
|
|
55
|
+
parent: Workcard, kind: WorkcardKind, type: string, view?: PlanningSchemaRegistry
|
|
56
|
+
): void => {
|
|
57
|
+
if (kind === WorkcardKind.Specification) {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
const registry = view ?? runtime.service().schemas
|
|
61
|
+
const parentType = registry.type(parent.type) as ProjectTypeSchema
|
|
62
|
+
const childType = registry.has(type) ? registry.type(type) : undefined
|
|
63
|
+
if (childType?.parents?.types != null && !childType.parents.types.includes(parent.type)) throw new CardTypeNotAllowed(`${type}:parent:${parent.type}`)
|
|
64
|
+
if (parentType.children != null) {
|
|
65
|
+
if (!parentType.children.types.includes(type)) throw new CardTypeNotAllowed(`${parent.type}:${type}`)
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
if (!cardHelper.isProject(parent)) throw new CardTypeNotAllowed(`${parent.type}:${type}`)
|
|
69
|
+
const allowed = kind === WorkcardKind.Project ? parentType.projectTypes ?? [] : parentType.cardTypes ?? []
|
|
70
|
+
if (allowed.includes(type)) {
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
if (kind === WorkcardKind.Card && parentType.scopedCardTypes === true && dataDefined(view, type)) {
|
|
74
|
+
return
|
|
75
|
+
}
|
|
77
76
|
throw new CardTypeNotAllowed(`${parent.type}:${type}`)
|
|
78
77
|
}
|
|
79
|
-
}
|
|
80
78
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
79
|
+
const childViewOf = async (
|
|
80
|
+
entityId: string, parent: Workcard
|
|
81
|
+
): Promise<PlanningSchemaRegistry | undefined> =>
|
|
82
|
+
runtime.schemaStore() == null ? undefined : await runtime.schemasFor(entityId, await hierarchy.project(parent))
|
|
83
|
+
|
|
84
|
+
const resolveExecution = async (
|
|
85
|
+
facade: PlanningFacade, exec: TransitionExecution
|
|
86
|
+
): Promise<Resolved> => {
|
|
87
|
+
const service = runtime.service()
|
|
88
|
+
const entityId = facade.scope.entityId
|
|
89
|
+
const create = exec.action === TransitionAction.Create
|
|
90
|
+
// Without a schema port every step reads the code registry itself, in today's order; with one,
|
|
91
|
+
// the card's project is found first and the step reads that project's resolved layer.
|
|
92
|
+
const layered = runtime.schemaStore() != null
|
|
93
|
+
let schemas: PlanningSchemaRegistry = service.schemas
|
|
94
|
+
|
|
95
|
+
let card: Workcard | undefined
|
|
96
|
+
let type: AnyTypeSchema
|
|
97
|
+
let parent: Workcard | undefined
|
|
98
|
+
let project: string | undefined
|
|
99
|
+
|
|
100
|
+
if (create) {
|
|
101
|
+
if (exec.card == null || typeof exec.card !== 'object') {
|
|
102
|
+
throw new PlanningError('malformed:create-without-draft')
|
|
103
|
+
}
|
|
104
|
+
const draft = exec.card
|
|
105
|
+
const parents = cardHelper.normalizeParents(draft.parent, draft.parents)
|
|
106
|
+
const loaded = new Map<string, Workcard>()
|
|
107
|
+
if (layered) {
|
|
108
|
+
// Loaded through the facade, so a parent this scope cannot see is absent here too.
|
|
109
|
+
const primary = parents[0] != null ? await facade.cards.load(parents[0]) : null
|
|
110
|
+
if (parents[0] != null && primary == null) {
|
|
111
|
+
throw new ParentNotFound(parents[0])
|
|
112
|
+
}
|
|
113
|
+
if (primary != null) {
|
|
114
|
+
loaded.set(parents[0], primary)
|
|
115
|
+
}
|
|
116
|
+
const project = draft.kind === WorkcardKind.Project || primary == null
|
|
117
|
+
? undefined
|
|
118
|
+
: await hierarchy.project({ kind: draft.kind, entityId, parent: parents[0], parents } as unknown as Workcard)
|
|
119
|
+
schemas = await runtime.schemasFor(entityId, project)
|
|
120
|
+
}
|
|
121
|
+
type = schemas.type(draft.type)
|
|
122
|
+
if (type.kind !== draft.kind) {
|
|
123
|
+
throw new PlanningError(`malformed:kind:${draft.type}:${draft.kind}`)
|
|
124
|
+
}
|
|
125
|
+
if (layered && isScoped(schemas) && schemas.isRetired(PlanningSchemaKind.Type, draft.type)) {
|
|
126
|
+
// Existing cards keep resolving a retired type; nothing new is made of it.
|
|
127
|
+
throw new UnknownWorkcardType(`retired:${draft.type}`)
|
|
128
|
+
}
|
|
129
|
+
// Normalized: the primary parent is first. It must accept the child; so must every project
|
|
130
|
+
// among the secondary parents.
|
|
131
|
+
for (const [index, id] of parents.entries()) {
|
|
132
|
+
const found = loaded.get(id) ?? await facade.cards.load(id)
|
|
133
|
+
if (found == null) {
|
|
134
|
+
throw new ParentNotFound(id)
|
|
135
|
+
}
|
|
136
|
+
if (index === 0) {
|
|
137
|
+
parent = found
|
|
138
|
+
}
|
|
139
|
+
if (found != null) {
|
|
140
|
+
const view = !layered ? undefined : index === 0 && cardHelper.isProject(found) ? schemas : await childViewOf(entityId, found)
|
|
141
|
+
assertChildAllowed(found, draft.kind, draft.type, view)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (draft.kind === WorkcardKind.Specification && parent == null) {
|
|
145
|
+
throw new PlanningError('malformed:specification-without-parent')
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
if (typeof exec.card !== 'string' || exec.card === '') {
|
|
149
|
+
throw new PlanningError(`malformed:${exec.action}-without-card`)
|
|
150
|
+
}
|
|
151
|
+
const found = await runtime.reader().cards.get(exec.card, entityId)
|
|
112
152
|
if (found == null) {
|
|
113
|
-
throw new
|
|
153
|
+
throw new WorkcardNotFound(exec.card)
|
|
114
154
|
}
|
|
115
|
-
if (
|
|
116
|
-
|
|
155
|
+
if (found.entityId !== entityId) {
|
|
156
|
+
throw new PlanningScopeMismatch(exec.card)
|
|
117
157
|
}
|
|
118
|
-
if (
|
|
119
|
-
|
|
158
|
+
if (facade.scope.projects != null && await facade.cards.load(exec.card) == null) {
|
|
159
|
+
// Outside the scope's projects: absent, exactly like another entity's card.
|
|
160
|
+
throw new WorkcardNotFound(exec.card)
|
|
161
|
+
}
|
|
162
|
+
card = found
|
|
163
|
+
if (layered) {
|
|
164
|
+
parent = card.parent != null ? await facade.cards.load(card.parent) ?? undefined : undefined
|
|
165
|
+
schemas = await runtime.schemasFor(entityId, await hierarchy.project(card))
|
|
166
|
+
type = schemas.type(card.type)
|
|
167
|
+
} else {
|
|
168
|
+
type = schemas.type(card.type)
|
|
169
|
+
parent = card.parent != null ? await facade.cards.load(card.parent) ?? undefined : undefined
|
|
120
170
|
}
|
|
121
171
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const found = await runtime.reader().cards.get(exec.card, entityId)
|
|
130
|
-
if (found == null) {
|
|
131
|
-
throw new WorkcardNotFound(exec.card)
|
|
172
|
+
|
|
173
|
+
project = card != null ? await hierarchy.project(card)
|
|
174
|
+
: (exec.card as WorkcardDraft).kind === WorkcardKind.Project ? undefined
|
|
175
|
+
: await hierarchy.project({ ...(exec.card as WorkcardDraft), entityId } as Workcard)
|
|
176
|
+
const flowId = exec.flow ?? statusHelper.primaryFlowOf(type)
|
|
177
|
+
if (!type.flows.includes(flowId)) {
|
|
178
|
+
throw new UnknownStatusFlow(`${type.type}:${flowId}`)
|
|
132
179
|
}
|
|
133
|
-
|
|
134
|
-
|
|
180
|
+
|
|
181
|
+
const kind = create ? (exec.card as WorkcardDraft).kind : card!.kind
|
|
182
|
+
const category = create ? (exec.card as WorkcardDraft).category : (card as Specification | undefined)?.category
|
|
183
|
+
const slot = kind === WorkcardKind.Specification && parent != null && category != null
|
|
184
|
+
? specificationHelper.slotOf(schemas.type(parent.type), category)
|
|
185
|
+
: undefined
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
create,
|
|
189
|
+
project,
|
|
190
|
+
...(card != null ? { card } : {}),
|
|
191
|
+
type,
|
|
192
|
+
flowId,
|
|
193
|
+
flow: schemas.flow(flowId),
|
|
194
|
+
...(parent != null ? { parent } : {}),
|
|
195
|
+
...(slot != null ? { slot } : {}),
|
|
196
|
+
store: service.store(type.type),
|
|
197
|
+
schemas,
|
|
135
198
|
}
|
|
136
|
-
card = found
|
|
137
|
-
type = schemas.type(card.type)
|
|
138
|
-
parent = card.parent != null ? await facade.cards.load(card.parent) ?? undefined : undefined
|
|
139
199
|
}
|
|
140
200
|
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
201
|
+
const projectFor = (resolved: Resolved, exec: TransitionExecution, cardId: string): string | undefined => {
|
|
202
|
+
if (resolved.card != null) {
|
|
203
|
+
return cardHelper.isProject(resolved.card) ? resolved.card.id : resolved.project
|
|
204
|
+
}
|
|
205
|
+
const draft = exec.card as WorkcardDraft
|
|
206
|
+
if (draft.kind === WorkcardKind.Project) {
|
|
207
|
+
return cardId
|
|
208
|
+
}
|
|
209
|
+
return resolved.project
|
|
144
210
|
}
|
|
145
211
|
|
|
146
|
-
|
|
147
|
-
const category = create ? (exec.card as WorkcardDraft).category : (card as Specification | undefined)?.category
|
|
148
|
-
const slot = kind === WorkcardKind.Specification && parent != null && category != null
|
|
149
|
-
? slotOf(schemas.type(parent.type), category)
|
|
150
|
-
: undefined
|
|
151
|
-
|
|
152
|
-
return {
|
|
153
|
-
create,
|
|
154
|
-
...(card != null ? { card } : {}),
|
|
155
|
-
type,
|
|
156
|
-
flowId,
|
|
157
|
-
flow: schemas.flow(flowId),
|
|
158
|
-
...(parent != null ? { parent } : {}),
|
|
159
|
-
...(slot != null ? { slot } : {}),
|
|
160
|
-
store: service.store(type.type),
|
|
161
|
-
}
|
|
212
|
+
return { normalizeExecution, assertChildAllowed, childViewOf, resolveExecution, projectFor }
|
|
162
213
|
}
|
|
163
214
|
|
|
164
|
-
/** The
|
|
165
|
-
export const
|
|
166
|
-
if (resolved.card != null) {
|
|
167
|
-
return isProject(resolved.card) ? resolved.card.id : projectOf(resolved.card, resolved.parent)
|
|
168
|
-
}
|
|
169
|
-
const draft = exec.card as WorkcardDraft
|
|
170
|
-
if (draft.kind === WorkcardKind.Project) {
|
|
171
|
-
return cardId
|
|
172
|
-
}
|
|
173
|
-
return projectOf({ kind: draft.kind, parent: draft.parent, parents: draft.parents ?? [] } as unknown as Workcard, resolved.parent)
|
|
174
|
-
}
|
|
215
|
+
/** The resolution steps of one service runtime — one per runtime. */
|
|
216
|
+
export const resolveUtilsOf = memoHelper.oncePer(makeResolveUtils)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { AnyTypeSchema, PlanningSchemaRegistry, PlanningStore, SpecificationSlot, StatusFlowSchema, Workcard } from '@owlmeans/planning'
|
|
2
|
+
|
|
3
|
+
/** Everything the steps after resolution read about an execution. */
|
|
4
|
+
export interface Resolved {
|
|
5
|
+
project?: string
|
|
6
|
+
create: boolean
|
|
7
|
+
/** The existing card (not a create). */
|
|
8
|
+
card?: Workcard
|
|
9
|
+
type: AnyTypeSchema
|
|
10
|
+
flowId: string
|
|
11
|
+
flow: StatusFlowSchema
|
|
12
|
+
/** The primary parent, when it resolves. */
|
|
13
|
+
parent?: Workcard
|
|
14
|
+
/** The slot a specification belongs to. */
|
|
15
|
+
slot?: SpecificationSlot
|
|
16
|
+
/** The store the type is written to. */
|
|
17
|
+
store: PlanningStore
|
|
18
|
+
/**
|
|
19
|
+
* The registry every later step reads: the service's code registry itself, or — where the store
|
|
20
|
+
* holds data-defined schemas — the resolved layer of the card's project.
|
|
21
|
+
*/
|
|
22
|
+
schemas: PlanningSchemaRegistry
|
|
23
|
+
}
|
package/src/executor/validate.ts
CHANGED
|
@@ -1,17 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from '
|
|
7
|
-
import type {
|
|
8
|
-
PlanningFacade, RelationshipDraft, Specification, TransitionExecution, WorkcardDraft,
|
|
9
|
-
} from '@owlmeans/planning'
|
|
10
|
-
import type { PlanningRuntime } from '../service.js'
|
|
11
|
-
import { assertChildAllowed } from './resolve.js'
|
|
12
|
-
import type { Resolved } from './resolve.js'
|
|
13
|
-
|
|
14
|
-
const ACTIONS = new Set<string>(Object.values(TransitionAction))
|
|
1
|
+
import { BODY_MAX, cardHelper, changesHelper, CODE_MAX, DESCRIPTION_MAX, IllegalTransition, LabelNotAllowed, MAX_LABELS, MAX_PARENTS, ParentNotFound, PlanningError, RelationshipRefused, specificationHelper, SpecificationRevisionConflict, SpecificationSlotUnknown, statusHelper, TITLE_MAX, TransitionAction, validateHelper, WorkcardKind, PlanningResourceKind, type PlanningFacade, type RelationshipDraft, type Specification, type TransitionExecution, type WorkcardDraft } from '@owlmeans/planning'
|
|
2
|
+
import type { PlanningRuntime } from '../types.js'
|
|
3
|
+
import { creatorHelper } from './creator.js'
|
|
4
|
+
import type { Resolved } from './types.js'
|
|
5
|
+
import { ACTIONS } from './consts.local.js'
|
|
6
|
+
import { resolveUtilsOf } from './resolve.js'
|
|
15
7
|
|
|
16
8
|
const malformed = (what: string): PlanningError => new PlanningError(`malformed:${what}`)
|
|
17
9
|
|
|
@@ -58,10 +50,10 @@ const assertShape = (exec: TransitionExecution, resolved: Resolved): void => {
|
|
|
58
50
|
assertText('code', changes.code ?? undefined, CODE_MAX, false)
|
|
59
51
|
}
|
|
60
52
|
|
|
61
|
-
const assertFlow = (exec: TransitionExecution, resolved: Resolved
|
|
53
|
+
const assertFlow = (exec: TransitionExecution, resolved: Resolved): void => {
|
|
62
54
|
if (resolved.create) {
|
|
63
55
|
const status = (exec.card as WorkcardDraft).status
|
|
64
|
-
const primary =
|
|
56
|
+
const primary = resolved.schemas.primaryFlow(resolved.type.type)
|
|
65
57
|
if (status != null && !primary.statuses.some(definition => definition.key === status)) {
|
|
66
58
|
throw new IllegalTransition(`${primary.id}:create:${status}`)
|
|
67
59
|
}
|
|
@@ -72,24 +64,24 @@ const assertFlow = (exec: TransitionExecution, resolved: Resolved, runtime: Plan
|
|
|
72
64
|
}
|
|
73
65
|
const card = resolved.card!
|
|
74
66
|
const from = card.flows[resolved.flowId] ?? card.status
|
|
75
|
-
if (ruleOf(resolved.flow, exec.transition!, from) == null) {
|
|
67
|
+
if (statusHelper.ruleOf(resolved.flow, exec.transition!, from) == null) {
|
|
76
68
|
throw new IllegalTransition(`${resolved.flowId}:${exec.transition}:${from}`)
|
|
77
69
|
}
|
|
78
70
|
}
|
|
79
71
|
|
|
80
|
-
const assertFields = (exec: TransitionExecution, resolved: Resolved
|
|
81
|
-
const schemas =
|
|
72
|
+
const assertFields = (exec: TransitionExecution, resolved: Resolved): void => {
|
|
73
|
+
const schemas = resolved.schemas
|
|
82
74
|
if (resolved.create) {
|
|
83
75
|
const draft = exec.card as WorkcardDraft
|
|
84
|
-
validateFields(schemas, resolved.type.type, mergeFields(draft.fields, exec.changes?.fields))
|
|
76
|
+
validateHelper.validateFields(schemas, resolved.type.type, changesHelper.mergeFields(draft.fields, exec.changes?.fields))
|
|
85
77
|
return
|
|
86
78
|
}
|
|
87
79
|
const clears = (exec.unset ?? []).filter(path => path.startsWith('fields.'))
|
|
88
80
|
if (exec.changes?.fields == null && clears.length === 0) {
|
|
89
81
|
return
|
|
90
82
|
}
|
|
91
|
-
const merged = applyUnset({ fields: mergeFields(resolved.card!.fields, exec.changes?.fields) }, clears)
|
|
92
|
-
validateFields(schemas, resolved.type.type, merged.fields)
|
|
83
|
+
const merged = changesHelper.applyUnset({ fields: changesHelper.mergeFields(resolved.card!.fields, exec.changes?.fields) }, clears)
|
|
84
|
+
validateHelper.validateFields(schemas, resolved.type.type, merged.fields)
|
|
93
85
|
}
|
|
94
86
|
|
|
95
87
|
const assertLabels = (exec: TransitionExecution, resolved: Resolved): void => {
|
|
@@ -117,7 +109,8 @@ const assertParentMove = async (
|
|
|
117
109
|
return
|
|
118
110
|
}
|
|
119
111
|
const changes = exec.changes ?? {}
|
|
120
|
-
const
|
|
112
|
+
const resolve = resolveUtilsOf(runtime)
|
|
113
|
+
const moved = [...new Set(cardHelper.normalizeParents(changes.parent, changes.parents))]
|
|
121
114
|
.filter(id => id !== resolved.card!.parent && !resolved.card!.parents.includes(id))
|
|
122
115
|
for (const id of moved) {
|
|
123
116
|
if (id === resolved.card!.id) {
|
|
@@ -127,18 +120,20 @@ const assertParentMove = async (
|
|
|
127
120
|
if (found == null) {
|
|
128
121
|
throw new ParentNotFound(id)
|
|
129
122
|
}
|
|
130
|
-
assertChildAllowed(
|
|
123
|
+
resolve.assertChildAllowed(
|
|
124
|
+
found, resolved.card!.kind, resolved.card!.type, await resolve.childViewOf(facade.scope.entityId, found)
|
|
125
|
+
)
|
|
131
126
|
}
|
|
132
127
|
}
|
|
133
128
|
|
|
134
129
|
const assertSpecification = async (
|
|
135
|
-
exec: TransitionExecution, resolved: Resolved,
|
|
130
|
+
exec: TransitionExecution, resolved: Resolved, facade: PlanningFacade
|
|
136
131
|
): Promise<void> => {
|
|
137
132
|
const kind = resolved.create ? (exec.card as WorkcardDraft).kind : resolved.card!.kind
|
|
138
133
|
if (kind !== WorkcardKind.Specification) {
|
|
139
134
|
return
|
|
140
135
|
}
|
|
141
|
-
const schemas =
|
|
136
|
+
const schemas = resolved.schemas
|
|
142
137
|
|
|
143
138
|
if (resolved.create) {
|
|
144
139
|
const draft = exec.card as WorkcardDraft
|
|
@@ -146,7 +141,7 @@ const assertSpecification = async (
|
|
|
146
141
|
if (draft.category == null || draft.category === '') {
|
|
147
142
|
throw malformed('specification-without-category')
|
|
148
143
|
}
|
|
149
|
-
const slot = slotOf(schemas.type(parent.type), draft.category)
|
|
144
|
+
const slot = specificationHelper.slotOf(schemas.type(parent.type), draft.category)
|
|
150
145
|
if (slot == null) {
|
|
151
146
|
throw new SpecificationSlotUnknown(`${parent.type}:${draft.category}`)
|
|
152
147
|
}
|
|
@@ -154,7 +149,7 @@ const assertSpecification = async (
|
|
|
154
149
|
throw malformed(`format:${draft.category}:${draft.format}`)
|
|
155
150
|
}
|
|
156
151
|
assertText('body', draft.body, BODY_MAX, false)
|
|
157
|
-
validateSpecificationBody(slot, draft.body)
|
|
152
|
+
validateHelper.validateSpecificationBody(slot, draft.body)
|
|
158
153
|
if (slot.multiple !== true && await facade.specifications.current(parent.id!, draft.category) != null) {
|
|
159
154
|
throw new SpecificationRevisionConflict(`${parent.id}:${draft.category}`)
|
|
160
155
|
}
|
|
@@ -175,7 +170,7 @@ const assertSpecification = async (
|
|
|
175
170
|
}
|
|
176
171
|
if (changes.body != null) {
|
|
177
172
|
assertText('body', changes.body, BODY_MAX, false)
|
|
178
|
-
validateSpecificationBody(slot, changes.body)
|
|
173
|
+
validateHelper.validateSpecificationBody(slot, changes.body)
|
|
179
174
|
}
|
|
180
175
|
}
|
|
181
176
|
|
|
@@ -207,15 +202,20 @@ const assertRelationships = async (
|
|
|
207
202
|
}
|
|
208
203
|
continue
|
|
209
204
|
}
|
|
210
|
-
const
|
|
205
|
+
const kind = rule.toKind ?? PlanningResourceKind.Workcard
|
|
206
|
+
if (draft.toKind != null && draft.toKind !== kind) throw new RelationshipRefused(`to-kind:${draft.type}`)
|
|
207
|
+
if (draft.fromKind != null && draft.fromKind !== PlanningResourceKind.Workcard) throw new RelationshipRefused(`from-kind:${draft.type}`)
|
|
208
|
+
draft.toKind = kind
|
|
209
|
+
const target = kind === PlanningResourceKind.Assignee ? await facade.assignees.load(draft.to)
|
|
210
|
+
: kind === PlanningResourceKind.Team ? await facade.teams.load(draft.to) : await facade.cards.load(draft.to)
|
|
211
211
|
if (target == null) {
|
|
212
212
|
throw new RelationshipRefused(`to:${draft.to}`)
|
|
213
213
|
}
|
|
214
214
|
if (rule.from != null && !rule.from.includes(resolved.type.type)) {
|
|
215
215
|
throw new RelationshipRefused(`from-type:${draft.type}:${resolved.type.type}`)
|
|
216
216
|
}
|
|
217
|
-
if (rule.to != null && !rule.to.includes(target.type)) {
|
|
218
|
-
throw new RelationshipRefused(`to-type:${draft.type}:${target.type}`)
|
|
217
|
+
if (rule.to != null && !rule.to.includes('type' in target ? target.type : '')) {
|
|
218
|
+
throw new RelationshipRefused(`to-type:${draft.type}:${'type' in target ? target.type : kind}`)
|
|
219
219
|
}
|
|
220
220
|
if (rule.single === true) {
|
|
221
221
|
const count = (counted.get(draft.type) ?? 0) + 1
|
|
@@ -232,8 +232,9 @@ const assertRelationships = async (
|
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
/**
|
|
235
|
-
* Step 6: shape, flow rule, immutables
|
|
236
|
-
*
|
|
235
|
+
* Step 6: shape, flow rule, immutables (`createdBy` among them, `creatorHelper.assertCreatorFixed`),
|
|
236
|
+
* `fields`, labels, parent moves, the specification slot and relationships — in that order.
|
|
237
|
+
* Nothing is written by a refusal here.
|
|
237
238
|
*
|
|
238
239
|
* @throws {PlanningError} `malformed:*` / `immutable:*`
|
|
239
240
|
* @throws {IllegalTransition | FieldsInvalid | LabelNotAllowed | ParentNotFound | CardTypeNotAllowed}
|
|
@@ -243,11 +244,12 @@ export const validateExecution = async (
|
|
|
243
244
|
runtime: PlanningRuntime, facade: PlanningFacade, exec: TransitionExecution, resolved: Resolved
|
|
244
245
|
): Promise<void> => {
|
|
245
246
|
assertShape(exec, resolved)
|
|
246
|
-
assertFlow(exec, resolved
|
|
247
|
-
assertMutable(exec, resolved.type)
|
|
248
|
-
|
|
247
|
+
assertFlow(exec, resolved)
|
|
248
|
+
changesHelper.assertMutable(exec, resolved.type)
|
|
249
|
+
creatorHelper.assertCreatorFixed(exec)
|
|
250
|
+
assertFields(exec, resolved)
|
|
249
251
|
assertLabels(exec, resolved)
|
|
250
252
|
await assertParentMove(exec, resolved, runtime, facade)
|
|
251
|
-
await assertSpecification(exec, resolved,
|
|
253
|
+
await assertSpecification(exec, resolved, facade)
|
|
252
254
|
await assertRelationships(exec, resolved, facade)
|
|
253
255
|
}
|