@owlmeans/server-planning 0.1.18-rc.8 → 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
package/src/executor.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
2
|
import type { BasicConfig, BasicContext } from '@owlmeans/context'
|
|
3
|
-
import { PlanningError, PlanningUnsupported, WorkcardConflict } from '@owlmeans/planning'
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from '@owlmeans/planning'
|
|
8
|
-
import { changeSetOf, transitionOf } from './executor/changes.js'
|
|
9
|
-
import { assignCode } from './executor/code.js'
|
|
3
|
+
import { applyHelper, PlanningError, PlanningUnsupported, WorkcardConflict, type ExecuteOptions, type PlanningExecContext, type PlanningFacade, type PlanningPlugin, type PlanningStore, type Transition, type TransitionExecution, type TransitionReceipt } from '@owlmeans/planning'
|
|
4
|
+
import { changesUtils } from './executor/changes.js'
|
|
5
|
+
import { codeUtils } from './executor/code.js'
|
|
6
|
+
import { creatorHelper } from './executor/creator.js'
|
|
10
7
|
import { makeReceipt } from './executor/receipt.js'
|
|
11
|
-
import {
|
|
12
|
-
import type { Resolved } from './executor/resolve.js'
|
|
8
|
+
import type { Resolved } from './executor/types.js'
|
|
13
9
|
import { validateExecution } from './executor/validate.js'
|
|
14
|
-
import type { PlanningRuntime } from './
|
|
10
|
+
import type { PlanningRuntime } from './types.js'
|
|
11
|
+
import { resolveUtilsOf } from './executor/resolve.js'
|
|
12
|
+
import { idHelper } from '@owlmeans/basic-ids'
|
|
13
|
+
|
|
14
|
+
export { assertCreatorFixed, createCreatorHelper, creatorHelper, creatorOf, withCreator } from './executor/creator.js'
|
|
15
|
+
export type * from './executor/creator/types.js'
|
|
15
16
|
|
|
16
17
|
const isoNow = (): string => new Date().toISOString()
|
|
17
18
|
|
|
@@ -25,7 +26,7 @@ const execContextOf = (
|
|
|
25
26
|
) => (plugin: PlanningPlugin): PlanningExecContext => ({
|
|
26
27
|
context: runtime.context() as BasicContext<BasicConfig>,
|
|
27
28
|
scope: facade.scope,
|
|
28
|
-
schemas:
|
|
29
|
+
schemas: resolved.schemas,
|
|
29
30
|
store: resolved.store,
|
|
30
31
|
facade,
|
|
31
32
|
plugin,
|
|
@@ -64,11 +65,13 @@ const currentReceipt = async (
|
|
|
64
65
|
/**
|
|
65
66
|
* THE write path. Nothing is appended before step 11, so every refusal leaves the log untouched.
|
|
66
67
|
*
|
|
67
|
-
* 1 normalize
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
68
|
+
* 1 normalize (a create's `createdBy` defaults to the scope's subject — `creatorHelper.withCreator`) →
|
|
69
|
+
* 2 scope → 3 idempotency (a known `key` answers its first receipt, before any validation) →
|
|
70
|
+
* 4 resolve → 5 the plugins' `before` chain (re-resolved once when it moved the type or the parent)
|
|
71
|
+
* → 6 validate (a `createdBy` in `changes`/`unset` is refused — `creatorHelper.assertCreatorFixed`) →
|
|
72
|
+
* 7 allocate the card id → 8 code → 9 changes (an update changing nothing answers
|
|
73
|
+
* the current receipt and appends nothing) → 10 seq, a CAS against the head → 11 append →
|
|
74
|
+
* 12 request the projection → 13 receipt → 14 `wait`.
|
|
72
75
|
*
|
|
73
76
|
* `after` hooks are NOT run here — the process that folds runs them, once per commit.
|
|
74
77
|
*/
|
|
@@ -76,10 +79,10 @@ export const executeTransition = async (
|
|
|
76
79
|
runtime: PlanningRuntime, facade: PlanningFacade, input: TransitionExecution, opts?: ExecuteOptions
|
|
77
80
|
): Promise<TransitionReceipt> => {
|
|
78
81
|
const scope = facade.scope
|
|
79
|
-
const service = runtime.service()
|
|
80
82
|
const at = (runtime.options.now ?? isoNow)()
|
|
83
|
+
const resolve = resolveUtilsOf(runtime)
|
|
81
84
|
|
|
82
|
-
let exec = normalizeExecution(input)
|
|
85
|
+
let exec = creatorHelper.withCreator(resolve.normalizeExecution(input), scope)
|
|
83
86
|
|
|
84
87
|
if (scope.entityId == null || scope.entityId === '') {
|
|
85
88
|
throw new PlanningError('malformed:scope-without-entity')
|
|
@@ -92,12 +95,12 @@ export const executeTransition = async (
|
|
|
92
95
|
}
|
|
93
96
|
}
|
|
94
97
|
|
|
95
|
-
let resolved = await resolveExecution(
|
|
98
|
+
let resolved = await resolve.resolveExecution(facade, exec)
|
|
96
99
|
|
|
97
100
|
const before = identityOf(exec)
|
|
98
|
-
exec = normalizeExecution(await runtime.registry.before(exec, execContextOf(runtime, facade, resolved)))
|
|
101
|
+
exec = resolve.normalizeExecution(await runtime.registry.before(exec, execContextOf(runtime, facade, resolved)))
|
|
99
102
|
if (identityOf(exec) !== before) {
|
|
100
|
-
resolved = await resolveExecution(
|
|
103
|
+
resolved = await resolve.resolveExecution(facade, exec)
|
|
101
104
|
}
|
|
102
105
|
|
|
103
106
|
await validateExecution(runtime, facade, exec, resolved)
|
|
@@ -109,24 +112,27 @@ export const executeTransition = async (
|
|
|
109
112
|
}
|
|
110
113
|
|
|
111
114
|
const cardId = resolved.create
|
|
112
|
-
? store.newId?.() ?? runtime.options.ids?.() ?? uuid()
|
|
115
|
+
? store.newId?.() ?? runtime.options.ids?.() ?? idHelper.uuid()
|
|
113
116
|
: resolved.card!.id!
|
|
114
117
|
|
|
115
|
-
exec = await assignCode(exec, resolved, scope, runtime.registry, execContextOf(runtime, facade, resolved))
|
|
118
|
+
exec = await codeUtils.assignCode(exec, resolved, scope, runtime.registry, execContextOf(runtime, facade, resolved))
|
|
116
119
|
|
|
117
|
-
const { set, empty } = changeSetOf(exec, resolved,
|
|
120
|
+
const { set, empty } = changesUtils.changeSetOf(exec, resolved, resolved.schemas, at)
|
|
118
121
|
if (empty) {
|
|
119
122
|
return await currentReceipt(store, resolved, scope.entityId, opts)
|
|
120
123
|
}
|
|
121
124
|
|
|
125
|
+
const preview = changesUtils.transitionOf({ exec, resolved, scope, set, cardId, seq: (resolved.card?.seq ?? 0) + 1, at, project: resolve.projectFor(resolved, exec, cardId) })
|
|
126
|
+
await store.validateProjection?.(resolved.card ?? null, applyHelper.applyTransition(resolved.card, preview), preview)
|
|
127
|
+
|
|
122
128
|
const seq = await transitions.nextSeq(cardId, resolved.create ? null : exec.expectSeq ?? null)
|
|
123
129
|
if (resolved.create && seq !== 1) {
|
|
124
130
|
throw new WorkcardConflict(`${cardId}:create:${seq}`)
|
|
125
131
|
}
|
|
126
132
|
|
|
127
|
-
const transition = transitionOf({
|
|
133
|
+
const transition = changesUtils.transitionOf({
|
|
128
134
|
exec, resolved, scope, set, cardId, seq, at,
|
|
129
|
-
project: projectFor(resolved, exec, cardId),
|
|
135
|
+
project: resolve.projectFor(resolved, exec, cardId),
|
|
130
136
|
})
|
|
131
137
|
|
|
132
138
|
let appended: Transition
|
package/src/facade.ts
CHANGED
|
@@ -1,14 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from '
|
|
5
|
-
import
|
|
6
|
-
CommitSource, PlanningFacade, PlanningScope, Specification, Workcard, WorkcardModel,
|
|
7
|
-
} from '@owlmeans/planning'
|
|
1
|
+
import { cardHelper, modelOf, PlanningResourceKind, PlanningUnsupported, queryHelper, specificationHelper, WorkcardKind, WorkcardNotFound, type CommitEvent, type CommitSource, type PlanningFacade, type PlanningScope, type Relationship, type Specification, type Transition, type Workcard, type WorkcardModel, type WorkcardQuery } from '@owlmeans/planning'
|
|
2
|
+
import { recordQueryHelper } from '@owlmeans/resource'
|
|
3
|
+
import type { Criteria } from '@owlmeans/resource'
|
|
4
|
+
import { makeResourceFacade } from './resources.js'
|
|
5
|
+
import { makeDefinitions } from './definitions.js'
|
|
8
6
|
import { executeTransition } from './executor.js'
|
|
9
|
-
import type { PlanningRuntime } from './
|
|
7
|
+
import type { PlanningRuntime } from './types.js'
|
|
8
|
+
import { makePlanningHierarchy } from './hierarchy.js'
|
|
10
9
|
import type { CommitHub } from './store/types.js'
|
|
11
10
|
|
|
11
|
+
/**
|
|
12
|
+
* The criteria a scope narrowed to `projects` adds to every card list, count and summary: the
|
|
13
|
+
* projects themselves, every card whose `parents` name one of them, and the specifications whose
|
|
14
|
+
* parent card is one of `through` — the cards of those projects a specification is seen through,
|
|
15
|
+
* exactly as a single read sees it (a specification's `parents` hold only its own card).
|
|
16
|
+
*/
|
|
17
|
+
export const projectCriteriaOf = (projects: readonly string[], through: readonly string[] = []): Criteria<Workcard> => ({
|
|
18
|
+
$or: [
|
|
19
|
+
{ id: { $in: [...projects] } },
|
|
20
|
+
{ parents: { $overlaps: [...projects] } },
|
|
21
|
+
...(through.length > 0 ? [{ kind: WorkcardKind.Specification, parent: { $in: [...through] } }] : []),
|
|
22
|
+
],
|
|
23
|
+
}) as Criteria<Workcard>
|
|
24
|
+
|
|
12
25
|
/**
|
|
13
26
|
* The scoped facade over the service's stores.
|
|
14
27
|
*
|
|
@@ -16,13 +29,44 @@ import type { CommitHub } from './store/types.js'
|
|
|
16
29
|
* a card of another entity is simply absent — `load` answers `null`, `get` throws
|
|
17
30
|
* `WorkcardNotFound`, exactly as for an id that never existed. Reads go through the composite
|
|
18
31
|
* store (the default store plus the stores plugins own); writes go through the executor.
|
|
32
|
+
*
|
|
33
|
+
* A scope naming `projects` sees those projects, the cards whose `parents` name one of them, and a
|
|
34
|
+
* specification whose parent card is one of those (a card's specification through its card);
|
|
35
|
+
* everything else reads as absent. Lists, counts and summaries are narrowed by an AND'd criteria
|
|
36
|
+
* that admits the same cards a single read does — the parent cards a specification is seen through
|
|
37
|
+
* are resolved once per call ({@link projectCriteriaOf}) — and commit events and transitions
|
|
38
|
+
* outside the set are dropped or refused.
|
|
39
|
+
*
|
|
40
|
+
* `definitions` is present when the default store holds data-defined schemas; a model is then
|
|
41
|
+
* built over the registry of the card's own project.
|
|
19
42
|
*/
|
|
20
43
|
export const makeStoreFacade = (runtime: PlanningRuntime, scope: PlanningScope): PlanningFacade => {
|
|
21
44
|
const entityId = scope.entityId
|
|
45
|
+
const projects = scope.projects != null ? [...new Set(scope.projects)] : undefined
|
|
22
46
|
const reader = () => runtime.reader()
|
|
23
47
|
const mine = <T extends { entityId: string }>(record: T | null | undefined): T | null =>
|
|
24
48
|
record != null && record.entityId === entityId ? record : null
|
|
25
49
|
|
|
50
|
+
const hierarchy = makePlanningHierarchy(runtime)
|
|
51
|
+
const visible = async (card: Workcard | null): Promise<Workcard | null> =>
|
|
52
|
+
card != null && await hierarchy.visible(card, projects) ? card : null
|
|
53
|
+
const allowedIds = async (): Promise<string[]> => {
|
|
54
|
+
if (projects == null) return []
|
|
55
|
+
const result = new Set<string>()
|
|
56
|
+
for (const project of projects) {
|
|
57
|
+
const root = mine(await reader().cards.get(project, entityId))
|
|
58
|
+
if (root == null) continue
|
|
59
|
+
result.add(project)
|
|
60
|
+
for (const card of await hierarchy.descendants(project, entityId)) result.add(card.id!)
|
|
61
|
+
}
|
|
62
|
+
return [...result]
|
|
63
|
+
}
|
|
64
|
+
const narrow = async (where: Criteria<Workcard>): Promise<Criteria<Workcard>> => projects == null ? where
|
|
65
|
+
: { ...where, $and: [...(where.$and ?? []), { id: { $in: await allowedIds() } }] }
|
|
66
|
+
const narrowed = async (query?: WorkcardQuery | null): Promise<Criteria<Workcard>> => await narrow(queryHelper.criteriaOf(query, scope))
|
|
67
|
+
const projectVisible = (project?: string | null): boolean =>
|
|
68
|
+
projects == null || (project != null && projects.includes(project))
|
|
69
|
+
|
|
26
70
|
const commitSource = (): CommitSource => {
|
|
27
71
|
const commits = reader().commits
|
|
28
72
|
if (commits == null) {
|
|
@@ -31,28 +75,37 @@ export const makeStoreFacade = (runtime: PlanningRuntime, scope: PlanningScope):
|
|
|
31
75
|
return commits
|
|
32
76
|
}
|
|
33
77
|
|
|
34
|
-
/** The transition is this entity's — its row, or the remembered commit of a purged one. */
|
|
78
|
+
/** The transition is this entity's (and this scope's) — its row, or the remembered commit of a purged one. */
|
|
79
|
+
const transitionVisible = async (row: Pick<Transition, 'card' | 'project'>): Promise<boolean> => {
|
|
80
|
+
if (projects == null) return true
|
|
81
|
+
const card = mine(await reader().cards.get(row.card, entityId))
|
|
82
|
+
return card != null ? await hierarchy.visible(card, projects) : projectVisible(row.project)
|
|
83
|
+
}
|
|
35
84
|
const assertTransition = async (transition: string): Promise<void> => {
|
|
36
85
|
const row = await reader().transitions?.get(transition)
|
|
37
86
|
if (row != null) {
|
|
38
|
-
if (row.entityId !== entityId) {
|
|
87
|
+
if (row.entityId !== entityId || !await transitionVisible(row)) {
|
|
39
88
|
throw new WorkcardNotFound(`transition:${transition}`)
|
|
40
89
|
}
|
|
41
90
|
return
|
|
42
91
|
}
|
|
43
92
|
const recalled = (reader().commits as Partial<CommitHub> | undefined)?.recall?.(transition)
|
|
44
|
-
if (recalled == null || recalled.entityId !== entityId) {
|
|
93
|
+
if (recalled == null || recalled.entityId !== entityId || !projectVisible(recalled.project)) {
|
|
45
94
|
throw new WorkcardNotFound(`transition:${transition}`)
|
|
46
95
|
}
|
|
47
96
|
}
|
|
48
97
|
|
|
98
|
+
/** The project a card resolves its data-defined types in. */
|
|
99
|
+
const projectFor = async (card: Workcard): Promise<string | undefined> => await hierarchy.project(card)
|
|
100
|
+
|
|
49
101
|
const facade: PlanningFacade = {
|
|
50
|
-
|
|
102
|
+
...makeResourceFacade(runtime, () => facade),
|
|
103
|
+
scope: Object.freeze({ ...scope, ...(projects != null ? { projects } : {}) }),
|
|
51
104
|
|
|
52
105
|
schemas: runtime.service().schemas,
|
|
53
106
|
|
|
54
107
|
cards: {
|
|
55
|
-
load: async id => mine(await reader().cards.get(id, entityId)),
|
|
108
|
+
load: async id => await visible(mine(await reader().cards.get(id, entityId))),
|
|
56
109
|
|
|
57
110
|
get: async id => {
|
|
58
111
|
const card = await facade.cards.load(id)
|
|
@@ -62,36 +115,42 @@ export const makeStoreFacade = (runtime: PlanningRuntime, scope: PlanningScope):
|
|
|
62
115
|
return card
|
|
63
116
|
},
|
|
64
117
|
|
|
65
|
-
list: async query => await reader().cards.list(
|
|
118
|
+
list: async query => await reader().cards.list(await narrowed(query), queryHelper.listOptionsOf(query)),
|
|
66
119
|
|
|
67
|
-
count: async query => await reader().cards.count(
|
|
120
|
+
count: async query => await reader().cards.count(await narrowed(query)),
|
|
68
121
|
|
|
69
122
|
summary: async (parents, query) => parents.length === 0
|
|
70
123
|
? {}
|
|
71
|
-
: await reader().cards.summary(parents, criteriaOf({ kind: query?.kind, type: query?.type }, scope)),
|
|
124
|
+
: await reader().cards.summary(parents, await narrow(queryHelper.criteriaOf({ kind: query?.kind, type: query?.type }, scope))),
|
|
72
125
|
},
|
|
73
126
|
|
|
74
127
|
specifications: {
|
|
75
128
|
current: async (parent, category) => {
|
|
129
|
+
if (projects != null && await facade.cards.load(parent) == null) {
|
|
130
|
+
return null
|
|
131
|
+
}
|
|
76
132
|
const specs = reader().specs
|
|
77
133
|
if (specs != null) {
|
|
78
134
|
return mine(await specs.current(parent, category, entityId))
|
|
79
135
|
}
|
|
80
|
-
const listed = await reader().cards.list(specCriteriaOf(parent, { category }, scope) as never, { size: 0 })
|
|
81
|
-
return currentSpecification(listed.items, category)
|
|
136
|
+
const listed = await reader().cards.list(queryHelper.specCriteriaOf(parent, { category }, scope) as never, { size: 0 })
|
|
137
|
+
return specificationHelper.currentSpecification(listed.items, category)
|
|
82
138
|
},
|
|
83
139
|
|
|
84
140
|
list: async (parent, query) => {
|
|
141
|
+
if (projects != null && await facade.cards.load(parent) == null) {
|
|
142
|
+
return { items: [], total: 0 }
|
|
143
|
+
}
|
|
85
144
|
const specs = reader().specs
|
|
86
145
|
if (specs != null) {
|
|
87
146
|
return await specs.list(parent, entityId, query)
|
|
88
147
|
}
|
|
89
|
-
return await reader().cards.list(specCriteriaOf(parent, query, scope) as never, listOptionsOf(query) as never) as never
|
|
148
|
+
return await reader().cards.list(queryHelper.specCriteriaOf(parent, query, scope) as never, queryHelper.listOptionsOf(query) as never) as never
|
|
90
149
|
},
|
|
91
150
|
|
|
92
151
|
get: async id => {
|
|
93
152
|
const card = await facade.cards.load(id)
|
|
94
|
-
if (card == null || !isSpecification(card)) {
|
|
153
|
+
if (card == null || !cardHelper.isSpecification(card)) {
|
|
95
154
|
throw new WorkcardNotFound(id)
|
|
96
155
|
}
|
|
97
156
|
return card as Specification
|
|
@@ -110,16 +169,44 @@ export const makeStoreFacade = (runtime: PlanningRuntime, scope: PlanningScope):
|
|
|
110
169
|
relationships: {
|
|
111
170
|
list: async query => {
|
|
112
171
|
const links = reader().links
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
172
|
+
if (links == null) {
|
|
173
|
+
return { items: [], total: 0 }
|
|
174
|
+
}
|
|
175
|
+
const where = queryHelper.linkWhereOf(query, scope)
|
|
176
|
+
// Inverse names are views over the canonical edge, never separately stored links.
|
|
177
|
+
const stored = await links.list(query?.type != null ? { entityId, id: where.id, project: where.project } : where, { size: 0 })
|
|
178
|
+
const names = query?.type == null ? undefined : Array.isArray(query.type) ? query.type : [query.type]
|
|
179
|
+
const presented: Relationship[] = []
|
|
180
|
+
for (const link of stored.items) {
|
|
181
|
+
if (names == null || names.includes(link.type)) presented.push(link)
|
|
182
|
+
if (names == null || (link.fromKind ?? PlanningResourceKind.Workcard) !== PlanningResourceKind.Workcard) continue
|
|
183
|
+
const source = await reader().cards.get(link.from, entityId)
|
|
184
|
+
if (source == null) continue
|
|
185
|
+
const rules = (await runtime.schemasFor(entityId, await hierarchy.project(source))).type(source.type).relationships ?? []
|
|
186
|
+
for (const rule of rules) if (rule.name === link.type && rule.inverse != null && names.includes(rule.inverse)) {
|
|
187
|
+
presented.push({ ...link, type: rule.inverse, from: link.to, to: link.from,
|
|
188
|
+
fromKind: link.toKind ?? PlanningResourceKind.Workcard, toKind: link.fromKind ?? PlanningResourceKind.Workcard })
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const items: Relationship[] = []
|
|
192
|
+
for (const link of presented) {
|
|
193
|
+
if (projects == null) { items.push(link); continue }
|
|
194
|
+
const endpoints: string[] = []
|
|
195
|
+
if ((link.fromKind ?? PlanningResourceKind.Workcard) === PlanningResourceKind.Workcard) endpoints.push(link.from)
|
|
196
|
+
if ((link.toKind ?? PlanningResourceKind.Workcard) === PlanningResourceKind.Workcard) endpoints.push(link.to)
|
|
197
|
+
if (endpoints.length === 0) continue
|
|
198
|
+
let admitted = true
|
|
199
|
+
for (const id of endpoints) if (await facade.cards.load(id) == null) { admitted = false; break }
|
|
200
|
+
if (admitted) items.push(link)
|
|
201
|
+
}
|
|
202
|
+
return recordQueryHelper.applyQuery(items, where as Criteria<Relationship>, queryHelper.listOptionsOf(query))
|
|
116
203
|
},
|
|
117
204
|
},
|
|
118
205
|
|
|
119
206
|
transitions: {
|
|
120
207
|
get: async id => {
|
|
121
208
|
const transition = mine(await reader().transitions?.get(id))
|
|
122
|
-
if (transition == null) {
|
|
209
|
+
if (transition == null || !await transitionVisible(transition)) {
|
|
123
210
|
throw new WorkcardNotFound(`transition:${id}`)
|
|
124
211
|
}
|
|
125
212
|
return transition
|
|
@@ -127,9 +214,13 @@ export const makeStoreFacade = (runtime: PlanningRuntime, scope: PlanningScope):
|
|
|
127
214
|
|
|
128
215
|
list: async query => {
|
|
129
216
|
const transitions = reader().transitions
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
217
|
+
if (transitions == null) {
|
|
218
|
+
return { items: [], total: 0 }
|
|
219
|
+
}
|
|
220
|
+
const listed = await transitions.list(queryHelper.transitionWhereOf(query, scope), { size: 0 })
|
|
221
|
+
const items: Transition[] = []
|
|
222
|
+
for (const transition of listed.items) if (await transitionVisible(transition)) items.push(transition)
|
|
223
|
+
return recordQueryHelper.applyQuery(items, undefined, queryHelper.listOptionsOf(query))
|
|
133
224
|
},
|
|
134
225
|
},
|
|
135
226
|
|
|
@@ -139,7 +230,10 @@ export const makeStoreFacade = (runtime: PlanningRuntime, scope: PlanningScope):
|
|
|
139
230
|
return await commitSource().status(transition)
|
|
140
231
|
},
|
|
141
232
|
|
|
142
|
-
subscribe: (listener, filter) => commitSource().subscribe(
|
|
233
|
+
subscribe: (listener, filter) => commitSource().subscribe(projects == null
|
|
234
|
+
? listener
|
|
235
|
+
: async (event: CommitEvent) => await transitionVisible(event) ? await listener(event) : undefined,
|
|
236
|
+
{ ...filter, entityId }),
|
|
143
237
|
|
|
144
238
|
wait: async (transition, opts) => {
|
|
145
239
|
await assertTransition(transition)
|
|
@@ -154,9 +248,18 @@ export const makeStoreFacade = (runtime: PlanningRuntime, scope: PlanningScope):
|
|
|
154
248
|
if (record.entityId !== entityId) {
|
|
155
249
|
throw new WorkcardNotFound(record.id ?? WorkcardKind.Card)
|
|
156
250
|
}
|
|
157
|
-
|
|
251
|
+
if (runtime.schemaStore() == null) {
|
|
252
|
+
return modelOf<T>(record, facade)
|
|
253
|
+
}
|
|
254
|
+
// A data-defined type answers from the registry of the card's own project.
|
|
255
|
+
const schemas = await runtime.schemasFor(entityId, await projectFor(record))
|
|
256
|
+
return modelOf<T>(record, { ...facade, schemas })
|
|
158
257
|
},
|
|
159
258
|
}
|
|
160
259
|
|
|
260
|
+
if (runtime.schemaStore() != null) {
|
|
261
|
+
facade.definitions = makeDefinitions(runtime, facade)
|
|
262
|
+
}
|
|
263
|
+
|
|
161
264
|
return facade
|
|
162
265
|
}
|
package/src/helper.ts
CHANGED
|
@@ -1,23 +1,29 @@
|
|
|
1
|
+
import { servePlanningResources } from './actions/resources.js'
|
|
1
2
|
import type { BasicConfig, BasicContext } from '@owlmeans/context'
|
|
2
3
|
import type { AbstractRequest } from '@owlmeans/entrypoint'
|
|
3
4
|
import type { PlanningFacade, PlanningProtocols, PlanningScope } from '@owlmeans/planning'
|
|
4
5
|
import { bind } from '@owlmeans/server-entrypoint'
|
|
5
6
|
import {
|
|
6
|
-
executePlanning, getCard, getCommit, getSpecification, getTransition, listCardSpecifications,
|
|
7
|
+
defineSchemas, executePlanning, getCard, getCommit, getSpecification, getTransition, listCardSpecifications,
|
|
7
8
|
listCardTransitions, listCards, listLinks, listSchemas, listSpecificationRevisions, summarizeCards,
|
|
8
9
|
watchCommits,
|
|
9
10
|
} from './actions/index.js'
|
|
10
11
|
import type { PlanningHandlerOptions } from './types.js'
|
|
11
|
-
import {
|
|
12
|
+
import { planningHandlerOf } from './utils/handler.js'
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Bind this package's handlers to a tree declared by `makePlanningProtocols` — one binding per
|
|
15
|
-
* protocol, the base included.
|
|
16
|
-
* wants its own handler for one leaf binds
|
|
16
|
+
* protocol, the base included (and `schema.define` when the tree declares it). Pass the tree
|
|
17
|
+
* itself, never a flattened list; an application that wants its own handler for one leaf binds
|
|
18
|
+
* that protocol afterwards.
|
|
17
19
|
*/
|
|
18
20
|
export const servePlanningEntrypoints = (protocols: PlanningProtocols, opts?: PlanningHandlerOptions) => [
|
|
21
|
+
...servePlanningResources(protocols, opts),
|
|
19
22
|
bind(protocols.base),
|
|
20
23
|
bind(protocols.schema.list, listSchemas(protocols.schema.list, opts)),
|
|
24
|
+
...(protocols.schema.define != null
|
|
25
|
+
? [bind(protocols.schema.define, defineSchemas(protocols.schema.define, opts))]
|
|
26
|
+
: []),
|
|
21
27
|
bind(protocols.card.list, listCards(protocols.card.list, opts)),
|
|
22
28
|
bind(protocols.card.summary, summarizeCards(protocols.card.summary, opts)),
|
|
23
29
|
bind(protocols.card.get, getCard(protocols.card.get, opts)),
|
|
@@ -32,13 +38,8 @@ export const servePlanningEntrypoints = (protocols: PlanningProtocols, opts?: Pl
|
|
|
32
38
|
bind(protocols.commit.events, watchCommits(protocols.commit.events, opts)),
|
|
33
39
|
]
|
|
34
40
|
|
|
35
|
-
/**
|
|
36
|
-
* The facade a hand-written handler works through: the request's entity and subject, plus `extra`
|
|
37
|
-
* (a `channel`) — never an entity from the request body.
|
|
38
|
-
*
|
|
39
|
-
* @throws {AuthorizationError} when the request carries no organization
|
|
40
|
-
*/
|
|
41
|
+
/** @deprecated compat:factory-refactor — use `planningHandlerOf(ctx).planningFor(…)` */
|
|
41
42
|
export const planningFor = (
|
|
42
43
|
ctx: BasicContext<BasicConfig>, req: AbstractRequest, extra?: Partial<PlanningScope>,
|
|
43
44
|
opts?: Pick<PlanningHandlerOptions, 'service'>
|
|
44
|
-
): PlanningFacade =>
|
|
45
|
+
): PlanningFacade => planningHandlerOf(ctx).planningFor(req, extra, opts)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Workcard } from '@owlmeans/planning'
|
|
2
|
+
|
|
3
|
+
export interface PlanningHierarchy {
|
|
4
|
+
ancestors: (card: Pick<Workcard, 'entityId' | 'parent' | 'parents'>) => Promise<Workcard[]>
|
|
5
|
+
project: (card: Pick<Workcard, 'entityId' | 'kind' | 'id' | 'parent'>) => Promise<string | undefined>
|
|
6
|
+
projects: (card: Workcard) => Promise<string[]>
|
|
7
|
+
visible: (card: Workcard, projects?: readonly string[]) => Promise<boolean>
|
|
8
|
+
descendants: (id: string, entityId: string) => Promise<Workcard[]>
|
|
9
|
+
/**
|
|
10
|
+
* A card's own documents: the specifications whose primary parent is the card, and theirs in
|
|
11
|
+
* turn. They never block the card's delete — its fold removes them with it.
|
|
12
|
+
*/
|
|
13
|
+
documents: (id: string, entityId: string) => Promise<Workcard[]>
|
|
14
|
+
/**
|
|
15
|
+
* The card's own shape against its schemas — or, `deleting`, whether it may go: a project always
|
|
16
|
+
* (its delete purges what is under it); any other card only when everything under it is one of
|
|
17
|
+
* its own `documents` (`hierarchy:parent-in-use` otherwise).
|
|
18
|
+
*/
|
|
19
|
+
validate: (card: Workcard, deleting?: boolean) => Promise<void>
|
|
20
|
+
}
|
package/src/hierarchy.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CardTypeNotAllowed, FieldsInvalid, ParentNotFound, PlanningError, PlanningSchemaKind, SchemaOrigin,
|
|
3
|
+
WorkcardKind, type Workcard, type PlanningSchemaRegistry, type ProjectTypeSchema, type ScopedSchemaRegistry,
|
|
4
|
+
} from '@owlmeans/planning'
|
|
5
|
+
import type { PlanningRuntime } from './types.js'
|
|
6
|
+
import type { PlanningHierarchy } from './hierarchy/types.js'
|
|
7
|
+
|
|
8
|
+
/** One recursive resolver for schema inheritance, project authorization and hierarchy constraints. */
|
|
9
|
+
export const makePlanningHierarchy = (runtime: PlanningRuntime): PlanningHierarchy => {
|
|
10
|
+
const cards = () => runtime.reader().cards
|
|
11
|
+
const ancestors: PlanningHierarchy['ancestors'] = async card => {
|
|
12
|
+
const visited = new Set<string>()
|
|
13
|
+
const result: Workcard[] = []
|
|
14
|
+
const visit = async (id: string, path: Set<string>): Promise<void> => {
|
|
15
|
+
if (path.has(id)) throw new PlanningError(`hierarchy:cycle:${id}`)
|
|
16
|
+
if (visited.has(id)) return
|
|
17
|
+
const parent = await cards().get(id, card.entityId)
|
|
18
|
+
if (parent == null || parent.entityId !== card.entityId) throw new ParentNotFound(id)
|
|
19
|
+
const next = new Set([...path, id])
|
|
20
|
+
for (const reference of parent.parents) await visit(reference, next)
|
|
21
|
+
visited.add(id)
|
|
22
|
+
result.push(parent)
|
|
23
|
+
}
|
|
24
|
+
for (const id of card.parents) await visit(id, new Set())
|
|
25
|
+
return result
|
|
26
|
+
}
|
|
27
|
+
const project: PlanningHierarchy['project'] = async card => {
|
|
28
|
+
if (card.kind === WorkcardKind.Project) return card.id
|
|
29
|
+
const visited = new Set<string>()
|
|
30
|
+
let parent = card.parent
|
|
31
|
+
while (parent != null) {
|
|
32
|
+
if (visited.has(parent)) throw new PlanningError(`hierarchy:cycle:${parent}`)
|
|
33
|
+
visited.add(parent)
|
|
34
|
+
const found = await cards().get(parent, card.entityId)
|
|
35
|
+
if (found == null) throw new ParentNotFound(parent)
|
|
36
|
+
if (found.kind === WorkcardKind.Project) return found.id
|
|
37
|
+
parent = found.parent
|
|
38
|
+
}
|
|
39
|
+
return undefined
|
|
40
|
+
}
|
|
41
|
+
const descendants: PlanningHierarchy['descendants'] = async (id, entityId) => {
|
|
42
|
+
const found = new Map<string, Workcard>()
|
|
43
|
+
let frontier = [id]
|
|
44
|
+
while (frontier.length > 0) {
|
|
45
|
+
const result = await cards().list({ entityId, parents: { $overlaps: frontier }, kind: [WorkcardKind.Card, WorkcardKind.Project, WorkcardKind.Specification] } as never, { size: 0 })
|
|
46
|
+
frontier = []
|
|
47
|
+
for (const card of result.items) if (card.id !== id && !found.has(card.id!)) { found.set(card.id!, card); frontier.push(card.id!) }
|
|
48
|
+
}
|
|
49
|
+
return [...found.values()]
|
|
50
|
+
}
|
|
51
|
+
/** The specifications among `below` whose primary parent is `id` or one already owned. */
|
|
52
|
+
const ownedOf = (id: string, below: Workcard[]): Workcard[] => {
|
|
53
|
+
const owners = new Set([id])
|
|
54
|
+
const owned: Workcard[] = []
|
|
55
|
+
let grown = true
|
|
56
|
+
while (grown) {
|
|
57
|
+
grown = false
|
|
58
|
+
for (const card of below) {
|
|
59
|
+
if (card.kind === WorkcardKind.Specification && card.parent != null && owners.has(card.parent) && !owners.has(card.id!)) {
|
|
60
|
+
owners.add(card.id!)
|
|
61
|
+
owned.push(card)
|
|
62
|
+
grown = true
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return owned
|
|
67
|
+
}
|
|
68
|
+
const assertPair = (parent: Workcard, card: Workcard, schemas: PlanningSchemaRegistry): void => {
|
|
69
|
+
if (card.kind === WorkcardKind.Specification) return
|
|
70
|
+
const childType = schemas.type(card.type)
|
|
71
|
+
if (childType.parents?.types != null && !childType.parents.types.includes(parent.type)) throw new CardTypeNotAllowed(`${card.type}:parent:${parent.type}`)
|
|
72
|
+
const parentType = schemas.type(parent.type)
|
|
73
|
+
if (parentType.children != null) {
|
|
74
|
+
if (!parentType.children.types.includes(card.type)) throw new CardTypeNotAllowed(`${parent.type}:child:${card.type}`)
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
if (parent.kind !== WorkcardKind.Project) throw new CardTypeNotAllowed(`${parent.type}:child:${card.type}`)
|
|
78
|
+
const container = parentType as ProjectTypeSchema
|
|
79
|
+
const allowed = card.kind === WorkcardKind.Project ? container.projectTypes ?? [] : container.cardTypes
|
|
80
|
+
const origin = (schemas as Partial<ScopedSchemaRegistry>).originOf?.(PlanningSchemaKind.Type, card.type)
|
|
81
|
+
if (!allowed.includes(card.type) && !(card.kind === WorkcardKind.Card && container.scopedCardTypes && (origin === SchemaOrigin.Project || origin === SchemaOrigin.Entity))) throw new CardTypeNotAllowed(`${parent.type}:${card.type}`)
|
|
82
|
+
}
|
|
83
|
+
const hierarchy: PlanningHierarchy = {
|
|
84
|
+
ancestors,
|
|
85
|
+
project,
|
|
86
|
+
descendants,
|
|
87
|
+
documents: async (id, entityId) => ownedOf(id, await descendants(id, entityId)),
|
|
88
|
+
projects: async card => [...new Set([...(card.kind === WorkcardKind.Project ? [card.id!] : []), ...(await ancestors(card)).filter(parent => parent.kind === WorkcardKind.Project).map(parent => parent.id!)])],
|
|
89
|
+
visible: async (card, projects) => projects == null || (await hierarchy.projects(card)).some(id => projects.includes(id)),
|
|
90
|
+
validate: async (card, deleting = false) => {
|
|
91
|
+
if (deleting) {
|
|
92
|
+
if (card.kind === WorkcardKind.Project) return
|
|
93
|
+
// The card's own documents go with it; anything else under it keeps it alive.
|
|
94
|
+
const below = await descendants(card.id!, card.entityId)
|
|
95
|
+
if (below.length > ownedOf(card.id!, below).length) throw new PlanningError(`hierarchy:parent-in-use:${card.id}`)
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
const schemas = await runtime.schemasFor(card.entityId, await project(card))
|
|
99
|
+
const type = schemas.type(card.type)
|
|
100
|
+
if (type.parents?.required && card.parent == null) throw new ParentNotFound(`required:${card.type}`)
|
|
101
|
+
if (card.parent != null && !card.parents.includes(card.parent)) throw new FieldsInvalid('hierarchy:primary-membership')
|
|
102
|
+
const parents = await ancestors(card)
|
|
103
|
+
if (parents.some(parent => parent.id === card.id)) throw new PlanningError(`hierarchy:cycle:${card.id}`)
|
|
104
|
+
for (const id of card.parents) {
|
|
105
|
+
const parent = parents.find(parent => parent.id === id)!
|
|
106
|
+
assertPair(parent, card, await runtime.schemasFor(card.entityId, await project(parent)))
|
|
107
|
+
}
|
|
108
|
+
if (card.parent != null) card.parentType = parents.find(parent => parent.id === card.parent)!.type
|
|
109
|
+
else delete card.parentType
|
|
110
|
+
},
|
|
111
|
+
}
|
|
112
|
+
return hierarchy
|
|
113
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Workcard, Transition } from '@owlmeans/planning'
|
|
2
|
+
|
|
3
|
+
export interface PlanningInvariants {
|
|
4
|
+
validate: (before: Workcard | null, after: Workcard | null, transition: Transition) => Promise<void>
|
|
5
|
+
references: (after: Workcard, transition: Transition) => Promise<void>
|
|
6
|
+
}
|