@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
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cardHelper, DEFAULT_COMMIT_TIMEOUT, PlanningForbidden, TransitionAction, WorkcardKind,
|
|
3
|
+
} from '@owlmeans/planning'
|
|
4
|
+
import type {
|
|
5
|
+
ExecuteOptions, ExecuteRequest, PlanningFacade, PlanningScope, TransitionExecution, Workcard,
|
|
6
|
+
} from '@owlmeans/planning'
|
|
7
|
+
import { creatorHelper } from '../executor/creator.js'
|
|
8
|
+
import type { PlanningAccess } from '../types.js'
|
|
9
|
+
import { makePlanningAccessModel } from '../utils/access.js'
|
|
10
|
+
import type { ExecutionHelper } from './execution/types.js'
|
|
11
|
+
|
|
12
|
+
export const createExecutionHelper = (): ExecutionHelper => {
|
|
13
|
+
const wireExecution = (body: ExecuteRequest, scope: PlanningScope): TransitionExecution => {
|
|
14
|
+
const { wait: _wait, timeout: _timeout, actor: _actor, ...exec } = body
|
|
15
|
+
if (exec.action === TransitionAction.Create && exec.card != null && typeof exec.card === 'object') {
|
|
16
|
+
const { createdBy: _createdBy, ...draft } = exec.card
|
|
17
|
+
exec.card = draft
|
|
18
|
+
}
|
|
19
|
+
return creatorHelper.withCreator(exec, scope)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const executeOptionsOf = (body: Pick<ExecuteRequest, 'wait' | 'timeout'>, maxPoll: number): ExecuteOptions => {
|
|
23
|
+
const ceiling = maxPoll * 1000
|
|
24
|
+
if (body.wait !== true) {
|
|
25
|
+
return {}
|
|
26
|
+
}
|
|
27
|
+
const asked = Number(body.timeout ?? DEFAULT_COMMIT_TIMEOUT)
|
|
28
|
+
return { wait: true, timeout: Math.min(Number.isFinite(asked) && asked > 0 ? asked : DEFAULT_COMMIT_TIMEOUT, ceiling) }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const assertExecutionWrites = async (
|
|
32
|
+
facade: PlanningFacade, exec: TransitionExecution, access?: PlanningAccess
|
|
33
|
+
): Promise<void> => {
|
|
34
|
+
if (access?.writes == null) {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
const model = makePlanningAccessModel(access)
|
|
38
|
+
const admitted = async (card: Workcard, seen = new Set<string>()): Promise<boolean> => {
|
|
39
|
+
if (model.writableIn(card)) return true
|
|
40
|
+
if (card.id != null && seen.has(card.id)) return false
|
|
41
|
+
if (card.id != null) seen.add(card.id)
|
|
42
|
+
for (const id of card.parents) {
|
|
43
|
+
const parent = await facade.cards.load(id)
|
|
44
|
+
if (parent != null && await admitted(parent, seen)) return true
|
|
45
|
+
}
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (exec.action === TransitionAction.Create && exec.card != null && typeof exec.card === 'object') {
|
|
50
|
+
if (exec.card.kind === WorkcardKind.Project) {
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
const parents = cardHelper.normalizeParents(exec.card.parent, exec.card.parents)
|
|
54
|
+
if (parents.length === 0) {
|
|
55
|
+
throw new PlanningForbidden('writes:root')
|
|
56
|
+
}
|
|
57
|
+
for (const id of parents) {
|
|
58
|
+
const parent = await facade.cards.load(id)
|
|
59
|
+
if (parent != null && !await admitted(parent)) {
|
|
60
|
+
throw new PlanningForbidden(`writes:${id}`)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
for (const id of cardHelper.normalizeParents(exec.changes?.parent, exec.changes?.parents)) {
|
|
66
|
+
const target = await facade.cards.load(id)
|
|
67
|
+
if (target != null && !await admitted(target)) throw new PlanningForbidden(`writes:destination:${id}`)
|
|
68
|
+
}
|
|
69
|
+
if (typeof exec.card === 'string' && exec.card !== '') {
|
|
70
|
+
const card = await facade.cards.load(exec.card)
|
|
71
|
+
if (card != null && !await admitted(card)) {
|
|
72
|
+
throw new PlanningForbidden(`writes:${exec.card}`)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const assertExecutionGranted = async (
|
|
78
|
+
facade: PlanningFacade, exec: TransitionExecution, access?: PlanningAccess
|
|
79
|
+
): Promise<void> => {
|
|
80
|
+
await assertExecutionWrites(facade, exec, access)
|
|
81
|
+
if (access?.grants == null) {
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
const model = makePlanningAccessModel(access)
|
|
85
|
+
if (exec.action === TransitionAction.Create && typeof exec.card === 'object' && exec.card?.kind === WorkcardKind.Project) {
|
|
86
|
+
model.assertGranted('createProjects', cardHelper.normalizeParents(exec.card.parent, exec.card.parents)[0])
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
if (exec.action === TransitionAction.Delete && typeof exec.card === 'string') {
|
|
90
|
+
const card = await facade.cards.load(exec.card)
|
|
91
|
+
if (card != null && cardHelper.isProject(card)) {
|
|
92
|
+
model.assertGranted('deleteProjects', card.id)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return { wireExecution, executeOptionsOf, assertExecutionWrites, assertExecutionGranted }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export const executionHelper = createExecutionHelper()
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { handlers } from '@owlmeans/server-api'
|
|
2
|
+
import type { PlanningProtocols } from '@owlmeans/planning'
|
|
3
|
+
import type { Context, PlanningHandlerOptions } from '../types.js'
|
|
4
|
+
import { guardHelper } from '../utils/guard.js'
|
|
5
|
+
import { planningHandlerOf } from '../utils/handler.js'
|
|
6
|
+
import type { RequestHandler } from './types.js'
|
|
7
|
+
|
|
8
|
+
/** @throws {WorkcardNotFound} for an absent card and for another entity's alike */
|
|
9
|
+
export const getCard = (protocol: PlanningProtocols['card']['get'], opts?: PlanningHandlerOptions): RequestHandler =>
|
|
10
|
+
handlers<Context>().request(protocol, async (req, ctx) => guardHelper.reply(async () => {
|
|
11
|
+
const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
|
|
12
|
+
|
|
13
|
+
return await facade.cards.get(`${req.params.id}`)
|
|
14
|
+
}))
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { handlers } from '@owlmeans/server-api'
|
|
2
|
+
import type { PlanningProtocols } from '@owlmeans/planning'
|
|
3
|
+
import type { Context, PlanningHandlerOptions } from '../types.js'
|
|
4
|
+
import { guardHelper } from '../utils/guard.js'
|
|
5
|
+
import { planningHandlerOf } from '../utils/handler.js'
|
|
6
|
+
import type { RequestHandler } from './types.js'
|
|
7
|
+
|
|
8
|
+
/** @throws {WorkcardNotFound} */
|
|
9
|
+
export const getSpecification = (protocol: PlanningProtocols['spec']['get'], opts?: PlanningHandlerOptions): RequestHandler =>
|
|
10
|
+
handlers<Context>().request(protocol, async (req, ctx) => guardHelper.reply(async () => {
|
|
11
|
+
const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
|
|
12
|
+
|
|
13
|
+
return await facade.specifications.get(`${req.params.id}`)
|
|
14
|
+
}))
|
package/src/actions/index.ts
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
export * from './
|
|
1
|
+
export type * from './execution/types.js'
|
|
2
|
+
|
|
3
|
+
export * from './apply-schema-request.js'
|
|
2
4
|
export * from './commit.js'
|
|
5
|
+
export * from './define-schemas.js'
|
|
3
6
|
export * from './execute.js'
|
|
7
|
+
export * from './execution.js'
|
|
8
|
+
export * from './get-card.js'
|
|
9
|
+
export * from './get-specification.js'
|
|
4
10
|
export * from './links.js'
|
|
5
|
-
export * from './
|
|
6
|
-
export * from './
|
|
11
|
+
export * from './list-card-specifications.js'
|
|
12
|
+
export * from './list-card-transitions.js'
|
|
13
|
+
export * from './list-cards.js'
|
|
14
|
+
export * from './list-schemas.js'
|
|
15
|
+
export * from './list-specification-revisions.js'
|
|
16
|
+
export * from './summarize-cards.js'
|
|
7
17
|
export * from './transitions.js'
|
|
8
18
|
export * from './watch.js'
|
package/src/actions/links.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { handlers } from '@owlmeans/server-api'
|
|
2
|
-
import {
|
|
2
|
+
import { wireHelper } from '@owlmeans/planning'
|
|
3
3
|
import type { PlanningProtocols } from '@owlmeans/planning'
|
|
4
4
|
import type { Context, PlanningHandlerOptions } from '../types.js'
|
|
5
|
-
import {
|
|
5
|
+
import { guardHelper } from '../utils/guard.js'
|
|
6
|
+
import { planningHandlerOf } from '../utils/handler.js'
|
|
7
|
+
import type { RequestHandler } from './types.js'
|
|
6
8
|
|
|
7
9
|
export const listLinks = (
|
|
8
10
|
protocol: PlanningProtocols['link']['list'], opts?: PlanningHandlerOptions
|
|
9
|
-
):
|
|
10
|
-
handlers<Context>().request(protocol, async (req, ctx) =>
|
|
11
|
-
const facade = await handlerFacade(
|
|
11
|
+
): RequestHandler =>
|
|
12
|
+
handlers<Context>().request(protocol, async (req, ctx) => guardHelper.reply(async () => {
|
|
13
|
+
const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
|
|
12
14
|
|
|
13
|
-
return await facade.relationships.list(decodeRelationshipQuery(req.query))
|
|
15
|
+
return await facade.relationships.list(wireHelper.decodeRelationshipQuery(req.query))
|
|
14
16
|
}))
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { handlers } from '@owlmeans/server-api'
|
|
2
|
+
import { wireHelper, type PlanningProtocols } from '@owlmeans/planning'
|
|
3
|
+
import type { Context, PlanningHandlerOptions } from '../types.js'
|
|
4
|
+
import { guardHelper } from '../utils/guard.js'
|
|
5
|
+
import { planningHandlerOf } from '../utils/handler.js'
|
|
6
|
+
import type { RequestHandler } from './types.js'
|
|
7
|
+
|
|
8
|
+
/** One card's specifications — the current document per category unless `all`. */
|
|
9
|
+
export const listCardSpecifications = (protocol: PlanningProtocols['card']['specifications'], opts?: PlanningHandlerOptions): RequestHandler =>
|
|
10
|
+
handlers<Context>().request(protocol, async (req, ctx) => guardHelper.reply(async () => {
|
|
11
|
+
const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
|
|
12
|
+
const card = await facade.cards.get(`${req.params.id}`)
|
|
13
|
+
|
|
14
|
+
return await facade.specifications.list(card.id!, wireHelper.decodeSpecificationQuery(req.query))
|
|
15
|
+
}))
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { handlers } from '@owlmeans/server-api'
|
|
2
|
+
import { wireHelper, type PlanningProtocols } from '@owlmeans/planning'
|
|
3
|
+
import type { Context, PlanningHandlerOptions } from '../types.js'
|
|
4
|
+
import { guardHelper } from '../utils/guard.js'
|
|
5
|
+
import { planningHandlerOf } from '../utils/handler.js'
|
|
6
|
+
import type { RequestHandler } from './types.js'
|
|
7
|
+
|
|
8
|
+
/** One card's log. */
|
|
9
|
+
export const listCardTransitions = (protocol: PlanningProtocols['card']['transitions'], opts?: PlanningHandlerOptions): RequestHandler =>
|
|
10
|
+
handlers<Context>().request(protocol, async (req, ctx) => guardHelper.reply(async () => {
|
|
11
|
+
const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
|
|
12
|
+
const card = await facade.cards.get(`${req.params.id}`)
|
|
13
|
+
|
|
14
|
+
return await facade.transitions.list({ ...wireHelper.decodeTransitionQuery(req.query), card: card.id })
|
|
15
|
+
}))
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { handlers } from '@owlmeans/server-api'
|
|
2
|
+
import { wireHelper, type PlanningProtocols } from '@owlmeans/planning'
|
|
3
|
+
import type { Context, PlanningHandlerOptions } from '../types.js'
|
|
4
|
+
import { guardHelper } from '../utils/guard.js'
|
|
5
|
+
import { planningHandlerOf } from '../utils/handler.js'
|
|
6
|
+
import type { RequestHandler } from './types.js'
|
|
7
|
+
|
|
8
|
+
/** Cards of the caller's entity; the query arrives in its wire shape and is decoded here. */
|
|
9
|
+
export const listCards = (protocol: PlanningProtocols['card']['list'], opts?: PlanningHandlerOptions): RequestHandler =>
|
|
10
|
+
handlers<Context>().request(protocol, async (req, ctx) => guardHelper.reply(async () => {
|
|
11
|
+
const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
|
|
12
|
+
|
|
13
|
+
return await facade.cards.list(wireHelper.decodeWorkcardQuery(req.query))
|
|
14
|
+
}))
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { handlers } from '@owlmeans/server-api'
|
|
2
|
+
import type { PlanningProtocols, ScopedSchemaBundle } from '@owlmeans/planning'
|
|
3
|
+
import type { Context, PlanningHandlerOptions } from '../types.js'
|
|
4
|
+
import { guardHelper } from '../utils/guard.js'
|
|
5
|
+
import { planningHandlerOf } from '../utils/handler.js'
|
|
6
|
+
import type { RequestHandler } from './types.js'
|
|
7
|
+
|
|
8
|
+
const text = (value: unknown): string | undefined =>
|
|
9
|
+
value == null || value === '' ? undefined : `${Array.isArray(value) ? value[0] : value}`
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The schema bundle — what a client loads to answer `can()` with no round trip. Scoped like every
|
|
13
|
+
* other leaf (no organization, no bundle). Where the store holds data-defined schemas it is the
|
|
14
|
+
* resolved layer of the organization, or of the project the `project` query names (a project this
|
|
15
|
+
* scope can see); otherwise the service's code bundle.
|
|
16
|
+
*
|
|
17
|
+
* @throws {WorkcardNotFound} for a project this scope cannot see
|
|
18
|
+
*/
|
|
19
|
+
export const listSchemas = (
|
|
20
|
+
protocol: PlanningProtocols['schema']['list'], opts?: PlanningHandlerOptions
|
|
21
|
+
): RequestHandler =>
|
|
22
|
+
handlers<Context>().request(protocol, async (req, ctx) => guardHelper.reply(async (): Promise<ScopedSchemaBundle> => {
|
|
23
|
+
const { facade } = await planningHandlerOf(ctx).handlerScopeOf(req, opts)
|
|
24
|
+
if (facade.definitions == null) {
|
|
25
|
+
return facade.schemas.bundle()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return await facade.definitions.bundle(text((req.query as { project?: unknown } | undefined)?.project))
|
|
29
|
+
}))
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { handlers } from '@owlmeans/server-api'
|
|
2
|
+
import type { PlanningProtocols } from '@owlmeans/planning'
|
|
3
|
+
import type { Context, PlanningHandlerOptions } from '../types.js'
|
|
4
|
+
import { guardHelper } from '../utils/guard.js'
|
|
5
|
+
import { planningHandlerOf } from '../utils/handler.js'
|
|
6
|
+
import type { RequestHandler } from './types.js'
|
|
7
|
+
|
|
8
|
+
/** A document's history, newest first, replayed from its log. */
|
|
9
|
+
export const listSpecificationRevisions = (protocol: PlanningProtocols['spec']['revisions'], opts?: PlanningHandlerOptions): RequestHandler =>
|
|
10
|
+
handlers<Context>().request(protocol, async (req, ctx) => guardHelper.reply(async () => {
|
|
11
|
+
const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
|
|
12
|
+
const limit = req.query?.limit == null ? undefined : Number(req.query.limit)
|
|
13
|
+
|
|
14
|
+
return { items: await facade.specifications.revisions(`${req.params.id}`, Number.isFinite(limit) ? limit : undefined) }
|
|
15
|
+
}))
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { handlers } from '@owlmeans/server-api'
|
|
2
|
+
import { bind } from '@owlmeans/server-entrypoint'
|
|
3
|
+
import { FieldsInvalid, PlanningForbidden } from '@owlmeans/planning'
|
|
4
|
+
import type { AssigneeDraft, TeamDraft, CommentDraft, PlanningProtocols, PlanningResourceCommand, PlanningResourceReply } from '@owlmeans/planning'
|
|
5
|
+
import type { Context, PlanningHandlerOptions } from '../types.js'
|
|
6
|
+
import { guardHelper } from '../utils/guard.js'
|
|
7
|
+
import { planningHandlerOf } from '../utils/handler.js'
|
|
8
|
+
import { makePlanningAccessModel } from '../utils/access.js'
|
|
9
|
+
import { executionHelper } from './execution.js'
|
|
10
|
+
|
|
11
|
+
/** Dispatch the opt-in resources; attribution is taken only from the verified handler scope. */
|
|
12
|
+
export const servePlanningResources = (protocols: PlanningProtocols, opts?: PlanningHandlerOptions) =>
|
|
13
|
+
(['assignees', 'teams', 'comments', 'mentions'] as const).flatMap(name => {
|
|
14
|
+
const tree = protocols[name]
|
|
15
|
+
if (tree == null) return []
|
|
16
|
+
return [
|
|
17
|
+
bind(tree.get, handlers<Context>().request(tree.get as any, async (req, ctx) => guardHelper.reply(async () => {
|
|
18
|
+
const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
|
|
19
|
+
return await facade[name].get((req.params as { id: string }).id)
|
|
20
|
+
}))),
|
|
21
|
+
bind(tree.list, handlers<Context>().request(tree.list as any, async (req, ctx) => guardHelper.reply(async () => {
|
|
22
|
+
const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
|
|
23
|
+
let query: unknown = {}
|
|
24
|
+
try { query = JSON.parse((req.query as { query?: string })?.query ?? '{}') } catch { throw new FieldsInvalid('resource:query') }
|
|
25
|
+
if (query == null || typeof query !== 'object' || Array.isArray(query)) throw new FieldsInvalid('resource:query')
|
|
26
|
+
// entityId is never read from the wire; the scoped facade owns it.
|
|
27
|
+
return await facade[name].list(query as never)
|
|
28
|
+
}))),
|
|
29
|
+
bind(tree.write, handlers<Context>().request(tree.write as any, async (req, ctx) => guardHelper.reply(async (): Promise<PlanningResourceReply> => {
|
|
30
|
+
const { facade, access } = await planningHandlerOf(ctx).handlerScopeOf(req, opts)
|
|
31
|
+
const body = req.body as PlanningResourceCommand
|
|
32
|
+
const reading = ['members', 'projects', 'assignees'].includes(body.action)
|
|
33
|
+
const id = (): string => { if (!body.id) throw new FieldsInvalid('resource:id'); return body.id }
|
|
34
|
+
const version = (): { version: number } => {
|
|
35
|
+
if (!Number.isSafeInteger(body.version) || body.version! < 1) throw new FieldsInvalid('resource:version')
|
|
36
|
+
return { version: body.version! }
|
|
37
|
+
}
|
|
38
|
+
if (!reading && name === 'assignees') makePlanningAccessModel(access).assertGranted('manageAssignees')
|
|
39
|
+
if (!reading && name === 'teams') makePlanningAccessModel(access).assertGranted('manageTeams')
|
|
40
|
+
if (name === 'comments' || name === 'mentions') {
|
|
41
|
+
const card = body.action === 'create' ? body.draft?.card : (await facade.comments.get(id())).card
|
|
42
|
+
if (card == null) throw new FieldsInvalid('comment:card')
|
|
43
|
+
await executionHelper.assertExecutionWrites(facade, { card, action: 'update' as any }, access)
|
|
44
|
+
}
|
|
45
|
+
if (name === 'teams' && ['attach', 'detach'].includes(body.action)) {
|
|
46
|
+
if (!body.project) throw new FieldsInvalid('team:project')
|
|
47
|
+
await executionHelper.assertExecutionWrites(facade, { card: body.project, action: 'update' as any }, access)
|
|
48
|
+
}
|
|
49
|
+
switch (name) {
|
|
50
|
+
case 'assignees':
|
|
51
|
+
if (body.action === 'create') return await facade.assignees.create(body.draft as AssigneeDraft)
|
|
52
|
+
if (body.action === 'update') return await facade.assignees.update(id(), body.draft ?? {}, version())
|
|
53
|
+
if (body.action === 'retire') return await facade.assignees.retire(id(), version())
|
|
54
|
+
break
|
|
55
|
+
case 'teams':
|
|
56
|
+
if (body.action === 'create') return await facade.teams.create(body.draft as TeamDraft)
|
|
57
|
+
if (body.action === 'update') return await facade.teams.update(id(), body.draft ?? {}, version())
|
|
58
|
+
if (body.action === 'remove') { await facade.teams.remove(id(), version()); return null }
|
|
59
|
+
if (body.action === 'members') return await facade.teams.members(id())
|
|
60
|
+
if (body.action === 'projects') return await facade.teams.projects(id())
|
|
61
|
+
if (body.action === 'assignees') return await facade.teams.assignees(id())
|
|
62
|
+
if (body.action === 'addMember' || body.action === 'removeMember') {
|
|
63
|
+
if (!body.assignee) throw new FieldsInvalid('team:assignee')
|
|
64
|
+
await facade.teams[body.action](id(), body.assignee); return null
|
|
65
|
+
}
|
|
66
|
+
if (body.action === 'attach' || body.action === 'detach') { await facade.teams[body.action](id(), body.project!); return null }
|
|
67
|
+
break
|
|
68
|
+
case 'comments':
|
|
69
|
+
if (body.action === 'create') return await facade.comments.create(body.draft as CommentDraft)
|
|
70
|
+
if (body.action === 'update') return await facade.comments.update(id(), { body: body.draft?.body! }, version())
|
|
71
|
+
if (body.action === 'remove') { await facade.comments.remove(id(), version()); return null }
|
|
72
|
+
break
|
|
73
|
+
case 'mentions':
|
|
74
|
+
if (body.action === 'rebuild') return await facade.mentions.rebuild(id())
|
|
75
|
+
}
|
|
76
|
+
throw new PlanningForbidden(`resource:action:${name}:${body.action}`)
|
|
77
|
+
}))),
|
|
78
|
+
]
|
|
79
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { handlers } from '@owlmeans/server-api'
|
|
2
|
+
import { wireHelper, type PlanningProtocols } from '@owlmeans/planning'
|
|
3
|
+
import type { Context, PlanningHandlerOptions } from '../types.js'
|
|
4
|
+
import { guardHelper } from '../utils/guard.js'
|
|
5
|
+
import { planningHandlerOf } from '../utils/handler.js'
|
|
6
|
+
import type { RequestHandler } from './types.js'
|
|
7
|
+
|
|
8
|
+
/** Intrinsic counts of DIRECT children per parent. */
|
|
9
|
+
export const summarizeCards = (protocol: PlanningProtocols['card']['summary'], opts?: PlanningHandlerOptions): RequestHandler =>
|
|
10
|
+
handlers<Context>().request(protocol, async (req, ctx) => guardHelper.reply(async () => {
|
|
11
|
+
const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
|
|
12
|
+
const { parents, ...query } = wireHelper.decodeSummaryQuery(req.query)
|
|
13
|
+
|
|
14
|
+
return await facade.cards.summary(parents, query)
|
|
15
|
+
}))
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { handlers } from '@owlmeans/server-api'
|
|
2
2
|
import type { PlanningProtocols } from '@owlmeans/planning'
|
|
3
3
|
import type { Context, PlanningHandlerOptions } from '../types.js'
|
|
4
|
-
import {
|
|
4
|
+
import { guardHelper } from '../utils/guard.js'
|
|
5
|
+
import { planningHandlerOf } from '../utils/handler.js'
|
|
6
|
+
import type { RequestHandler } from './types.js'
|
|
5
7
|
|
|
6
8
|
/** @throws {WorkcardNotFound} for an absent transition and for another entity's alike */
|
|
7
9
|
export const getTransition = (
|
|
8
10
|
protocol: PlanningProtocols['transition']['get'], opts?: PlanningHandlerOptions
|
|
9
|
-
):
|
|
10
|
-
handlers<Context>().request(protocol, async (req, ctx) =>
|
|
11
|
-
const facade = await handlerFacade(
|
|
11
|
+
): RequestHandler =>
|
|
12
|
+
handlers<Context>().request(protocol, async (req, ctx) => guardHelper.reply(async () => {
|
|
13
|
+
const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
|
|
12
14
|
|
|
13
15
|
return await facade.transitions.get(`${req.params.transition}`)
|
|
14
16
|
}))
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { EntrypointProtocolDeclaration } from '@owlmeans/entrypoint'
|
|
2
|
+
import type { BoundEntrypointHandler } from '@owlmeans/server-entrypoint'
|
|
3
|
+
|
|
4
|
+
/** A handler bound to one protocol leaf — what `handlers().request()` and `connection()` answer. */
|
|
5
|
+
export interface RequestHandler extends BoundEntrypointHandler<EntrypointProtocolDeclaration> {}
|
package/src/actions/watch.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { logger } from '@owlmeans/log'
|
|
2
|
+
import { planningReplyHelper, PLANNING_COMMIT_EVENT } from '@owlmeans/planning'
|
|
2
3
|
import type { CommitEvent, CommitFeedQuery, PlanningProtocols } from '@owlmeans/planning'
|
|
3
4
|
import { connection } from '@owlmeans/server-socket'
|
|
4
5
|
import type { EventMessage } from '@owlmeans/socket'
|
|
5
6
|
import { MessageType } from '@owlmeans/socket'
|
|
6
7
|
import type { Context, PlanningHandlerOptions } from '../types.js'
|
|
7
|
-
import {
|
|
8
|
+
import { planningHandlerOf } from '../utils/handler.js'
|
|
9
|
+
import type { RequestHandler } from './types.js'
|
|
10
|
+
|
|
11
|
+
const log = logger('planning:watch')
|
|
8
12
|
|
|
9
13
|
const text = (value: unknown): string | undefined =>
|
|
10
14
|
value == null || value === '' ? undefined : `${value}`
|
|
@@ -18,8 +22,8 @@ const text = (value: unknown): string | undefined =>
|
|
|
18
22
|
*/
|
|
19
23
|
export const watchCommits = (
|
|
20
24
|
protocol: PlanningProtocols['commit']['events'], opts?: PlanningHandlerOptions
|
|
21
|
-
):
|
|
22
|
-
const facade = await handlerFacade(
|
|
25
|
+
): RequestHandler => connection<typeof protocol, Context>(protocol, async (conn, ctx, req) => {
|
|
26
|
+
const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
|
|
23
27
|
const query = (req.query ?? {}) as CommitFeedQuery
|
|
24
28
|
const frame = opts?.event ?? PLANNING_COMMIT_EVENT
|
|
25
29
|
const filter = {
|
|
@@ -30,9 +34,9 @@ export const watchCommits = (
|
|
|
30
34
|
const unsubscribe = await facade.commits.subscribe(async (event: CommitEvent) => {
|
|
31
35
|
try {
|
|
32
36
|
const enriched = event.record !== undefined ? event : { ...event, record: await facade.cards.load(event.card) }
|
|
33
|
-
await conn.notify(frame, enriched)
|
|
37
|
+
await conn.notify(frame, planningReplyHelper.project(enriched))
|
|
34
38
|
} catch (e) {
|
|
35
|
-
|
|
39
|
+
log.warn('Planning commit notify failed', e)
|
|
36
40
|
}
|
|
37
41
|
}, filter)
|
|
38
42
|
|
|
@@ -45,7 +49,7 @@ export const watchCommits = (
|
|
|
45
49
|
try {
|
|
46
50
|
unsubscribe()
|
|
47
51
|
} catch (e) {
|
|
48
|
-
|
|
52
|
+
log.warn('Planning commit unsubscribe failed', e)
|
|
49
53
|
}
|
|
50
54
|
}
|
|
51
55
|
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** The expectations a conformance case states — each throws `ConformanceFailure` when it does not hold. */
|
|
2
|
+
export interface AssertHelper {
|
|
3
|
+
/** @throws {ConformanceFailure} */
|
|
4
|
+
check: (condition: unknown, message: string) => void
|
|
5
|
+
/** Structural equality over JSON values. @throws {ConformanceFailure} */
|
|
6
|
+
same: (actual: unknown, expected: unknown, message: string) => void
|
|
7
|
+
/** The same members in any order. @throws {ConformanceFailure} */
|
|
8
|
+
sameSet: (actual: readonly unknown[], expected: readonly unknown[], message: string) => void
|
|
9
|
+
/**
|
|
10
|
+
* The promise rejects with an error of the class (matched by instance or by its registered type
|
|
11
|
+
* name, so a duplicate module copy counts). Answers the error.
|
|
12
|
+
*
|
|
13
|
+
* @throws {ConformanceFailure}
|
|
14
|
+
*/
|
|
15
|
+
rejects: (run: Promise<unknown> | (() => Promise<unknown>), expected: { typeName: string }, message: string) => Promise<Error>
|
|
16
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { changesHelper } from '@owlmeans/planning'
|
|
2
|
+
import type { AssertHelper } from './assert/types.js'
|
|
3
|
+
import { ConformanceFailure } from './errors.js'
|
|
4
|
+
|
|
5
|
+
export { ConformanceFailure } from './errors.js'
|
|
6
|
+
|
|
7
|
+
const show = (value: unknown): string => {
|
|
8
|
+
try {
|
|
9
|
+
return JSON.stringify(value)
|
|
10
|
+
} catch {
|
|
11
|
+
return `${value}`
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const createAssertHelper = (): AssertHelper => {
|
|
16
|
+
const check = (condition: unknown, message: string): void => {
|
|
17
|
+
if (condition !== true) {
|
|
18
|
+
throw new ConformanceFailure(message)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const same = (actual: unknown, expected: unknown, message: string): void => {
|
|
23
|
+
if (!changesHelper.sameValue(actual, expected)) {
|
|
24
|
+
throw new ConformanceFailure(`${message}: expected ${show(expected)}, got ${show(actual)}`)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const sameSet = (actual: readonly unknown[], expected: readonly unknown[], message: string): void => {
|
|
29
|
+
const sort = (values: readonly unknown[]) => [...values].map(show).sort()
|
|
30
|
+
same(sort(actual), sort(expected), message)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const rejects = async (
|
|
34
|
+
run: Promise<unknown> | (() => Promise<unknown>), expected: { typeName: string }, message: string
|
|
35
|
+
): Promise<Error> => {
|
|
36
|
+
try {
|
|
37
|
+
await (typeof run === 'function' ? run() : run)
|
|
38
|
+
} catch (error) {
|
|
39
|
+
const type = (error as { type?: unknown } | null)?.type
|
|
40
|
+
if (error instanceof (expected as unknown as abstract new (...args: never[]) => unknown)
|
|
41
|
+
|| (typeof type === 'string' && type === expected.typeName)) {
|
|
42
|
+
return error as Error
|
|
43
|
+
}
|
|
44
|
+
throw new ConformanceFailure(`${message}: expected ${expected.typeName}, got ${show((error as Error)?.message ?? error)}`)
|
|
45
|
+
}
|
|
46
|
+
throw new ConformanceFailure(`${message}: expected ${expected.typeName}, nothing was thrown`)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return { check, same, sameSet, rejects }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const assertHelper = createAssertHelper()
|
|
53
|
+
|
|
54
|
+
/** @deprecated compat:factory-refactor — use `assertHelper.check(…)` */
|
|
55
|
+
export const check = (condition: unknown, message: string): void => assertHelper.check(condition, message)
|
|
56
|
+
|
|
57
|
+
/** @deprecated compat:factory-refactor — use `assertHelper.same(…)` */
|
|
58
|
+
export const same = (actual: unknown, expected: unknown, message: string): void => assertHelper.same(actual, expected, message)
|
|
59
|
+
|
|
60
|
+
/** @deprecated compat:factory-refactor — use `assertHelper.sameSet(…)` */
|
|
61
|
+
export const sameSet = (actual: readonly unknown[], expected: readonly unknown[], message: string): void =>
|
|
62
|
+
assertHelper.sameSet(actual, expected, message)
|
|
63
|
+
|
|
64
|
+
/** @deprecated compat:factory-refactor — use `assertHelper.rejects(…)` */
|
|
65
|
+
export const rejects = async (
|
|
66
|
+
run: Promise<unknown> | (() => Promise<unknown>), expected: { typeName: string }, message: string
|
|
67
|
+
): Promise<Error> => await assertHelper.rejects(run, expected, message)
|