@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,7 @@
|
|
|
1
|
+
import { PlanningSchemaKind, type AssigneeTypeSchema, type StatusFlowSchema, type WorkcardTypeSchema } from '@owlmeans/planning'
|
|
2
|
+
|
|
3
|
+
export interface Planned {
|
|
4
|
+
kind: PlanningSchemaKind
|
|
5
|
+
key: string
|
|
6
|
+
definition: WorkcardTypeSchema | StatusFlowSchema | AssigneeTypeSchema
|
|
7
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type { BasicConfig, BasicContext, LazyService } from '@owlmeans/context'
|
|
2
2
|
import type { AbstractRequest } from '@owlmeans/entrypoint'
|
|
3
|
-
import type {
|
|
4
|
-
AnyTypeSchema, PlanningPlugin, PlanningScope, PlanningService, PlanningStore, StatusFlowSchema,
|
|
5
|
-
WithPlanningService,
|
|
6
|
-
} from '@owlmeans/planning'
|
|
3
|
+
import type { AssigneeTypeSchema, AnyTypeSchema, PlanningAppDeclarations, PlanningPlugin, PlanningScope, PlanningService, PlanningStore, StatusFlowSchema, WithPlanningService, PlanningSchemaRegistry, SchemaStore, ScopedSchemaRegistry, CommitEvent, PlanningExecContext, PlanningHookContext, TransitionExecution, WorkcardDraft } from '@owlmeans/planning'
|
|
7
4
|
import type { ApiServerAppend } from '@owlmeans/server-api'
|
|
8
5
|
import type { ServerConfig, ServerContext } from '@owlmeans/server-context'
|
|
6
|
+
import type { StoreRoute } from './store/types.js'
|
|
7
|
+
|
|
9
8
|
|
|
10
9
|
export interface Config extends ServerConfig { }
|
|
11
10
|
|
|
@@ -15,8 +14,11 @@ export interface PlanningServiceOptions {
|
|
|
15
14
|
/** The default store — every type no plugin owns. An in-memory store when omitted. */
|
|
16
15
|
store?: PlanningStore
|
|
17
16
|
plugins?: PlanningPlugin[]
|
|
18
|
-
/**
|
|
19
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Schemas registered before any plugin's; `app` is the first source every plugin's `schemas.app`
|
|
19
|
+
* is merged over.
|
|
20
|
+
*/
|
|
21
|
+
schemas?: { types?: AnyTypeSchema[], flows?: StatusFlowSchema[], assigneeTypes?: AssigneeTypeSchema[], app?: PlanningAppDeclarations }
|
|
20
22
|
/** Run the `after` chain in this process. Default `true`. */
|
|
21
23
|
hooks?: boolean
|
|
22
24
|
/** Card ids when the store mints none. */
|
|
@@ -37,6 +39,53 @@ export interface PlanningScopeExtractor {
|
|
|
37
39
|
(req: AbstractRequest, ctx: BasicContext<BasicConfig>): Partial<PlanningScope> | Promise<Partial<PlanningScope>>
|
|
38
40
|
}
|
|
39
41
|
|
|
42
|
+
/**
|
|
43
|
+
* A grant: `true` for everything the request can see, a list for those project cards only (the
|
|
44
|
+
* parent of a created project, the deleted project, the schema layer).
|
|
45
|
+
*/
|
|
46
|
+
export type PlanningGrant = boolean | string[]
|
|
47
|
+
|
|
48
|
+
export interface PlanningAccessGrants {
|
|
49
|
+
/** Create a project — at the root only with `true`; a list names the parent projects. */
|
|
50
|
+
manageAssignees?: boolean
|
|
51
|
+
manageTeams?: boolean
|
|
52
|
+
createProjects?: PlanningGrant
|
|
53
|
+
/** Delete a project — a list names the projects. */
|
|
54
|
+
deleteProjects?: PlanningGrant
|
|
55
|
+
/** Write data-defined types and flows — the organization-wide layer only with `true`. */
|
|
56
|
+
defineSchemas?: PlanningGrant
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** What a hosting application decides about one request. */
|
|
60
|
+
export interface PlanningAccess {
|
|
61
|
+
/** The organization the request acts in — the only source of it once a resolver is given. */
|
|
62
|
+
entityId: string
|
|
63
|
+
/** Narrows every read and write to these project cards. Every project when omitted. */
|
|
64
|
+
projects?: string[]
|
|
65
|
+
/**
|
|
66
|
+
* The project cards the request may WRITE in: `execute` on a card, specification or link inside
|
|
67
|
+
* one of them (the same rule a narrowed read admits — the project, the cards whose `parents` name
|
|
68
|
+
* it, a specification through its parent card), and `schema.define` of a project layer. A project
|
|
69
|
+
* outside `projects` is still unreachable. Omitted — writes reach every project `projects` admits;
|
|
70
|
+
* present — anything else is refused with `PlanningForbidden` (403). Creating a project is
|
|
71
|
+
* `grants.createProjects`' alone; the organization-wide schema layer is `grants.defineSchemas`'.
|
|
72
|
+
*/
|
|
73
|
+
writes?: string[]
|
|
74
|
+
/**
|
|
75
|
+
* Gates the writes above. Omitted — nothing is gated; present — a flag it leaves out is refused
|
|
76
|
+
* with `PlanningForbidden`.
|
|
77
|
+
*/
|
|
78
|
+
grants?: PlanningAccessGrants
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Resolves a request's access. A throw is the request's answer (an `AuthForbidden` answers 403
|
|
83
|
+
* like any other guard refusal).
|
|
84
|
+
*/
|
|
85
|
+
export interface PlanningAccessResolver {
|
|
86
|
+
(req: AbstractRequest, ctx: BasicContext<BasicConfig>): Promise<PlanningAccess>
|
|
87
|
+
}
|
|
88
|
+
|
|
40
89
|
export interface PlanningHandlerOptions {
|
|
41
90
|
/** The planning service alias. `PLANNING_SERVICE` when omitted. */
|
|
42
91
|
service?: string
|
|
@@ -45,4 +94,88 @@ export interface PlanningHandlerOptions {
|
|
|
45
94
|
/** The longest single long poll, in seconds — clamps `commit.get`'s `wait` and `execute`'s `timeout`. */
|
|
46
95
|
maxPoll?: number
|
|
47
96
|
scope?: PlanningScopeExtractor
|
|
97
|
+
/**
|
|
98
|
+
* The hosting application's access decision per request. Without it the organization is the
|
|
99
|
+
* request's own (`requireEntityKey`) and nothing is narrowed or gated.
|
|
100
|
+
*/
|
|
101
|
+
access?: PlanningAccessResolver
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface SchemaViewsOptions {
|
|
105
|
+
/** The code registry every layer starts from. */
|
|
106
|
+
code: () => PlanningSchemaRegistry
|
|
107
|
+
/** Bumps whenever the code registry changes (a plugin's `use`). */
|
|
108
|
+
version: () => number
|
|
109
|
+
/** The schema port, when the store has one. */
|
|
110
|
+
port: () => SchemaStore | undefined
|
|
111
|
+
/** How many resolved layers are kept. */
|
|
112
|
+
limit?: number
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface SchemaViews {
|
|
116
|
+
/** The code registry itself without a port; the resolved layer with one. */
|
|
117
|
+
of: (entityId: string, project?: string) => Promise<PlanningSchemaRegistry>
|
|
118
|
+
/** @throws {PlanningUnsupported} without a port */
|
|
119
|
+
scoped: (entityId: string, project?: string) => Promise<ScopedSchemaRegistry>
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface ProjectionOptions {
|
|
123
|
+
/** The planning service alias. */
|
|
124
|
+
service?: string
|
|
125
|
+
/** The store the queue folds — the service's default store when omitted. */
|
|
126
|
+
store?: (service: PlanningService) => PlanningStore
|
|
127
|
+
/** Where events go — the store's commit hub `publish` when it has one. */
|
|
128
|
+
publish?: (event: CommitEvent) => Promise<void>
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface PluginRegistry {
|
|
132
|
+
/**
|
|
133
|
+
* Register, or replace the plugin of the same `name`. Its schemas are contributed now; the
|
|
134
|
+
* application's declarations are re-merged from the service's own and every plugin's, in plugin
|
|
135
|
+
* order (`appSchemaHelper.merge`), so a replaced plugin's declarations leave with it.
|
|
136
|
+
*
|
|
137
|
+
* @throws {SchemaInvalid} a malformed `schemas.app` — the plugin is not registered
|
|
138
|
+
*/
|
|
139
|
+
use: (plugin: PlanningPlugin) => void
|
|
140
|
+
/** Every plugin, `order` ascending (registration order among equals). */
|
|
141
|
+
plugins: () => PlanningPlugin[]
|
|
142
|
+
/** The owning plugins that supply a store, in plugin order, each store resolved once. */
|
|
143
|
+
routes: (ctx?: BasicContext<BasicConfig>) => StoreRoute[]
|
|
144
|
+
/** The first owning plugin's store for a type, else `fallback`. */
|
|
145
|
+
storeFor: (type: string | undefined, fallback: PlanningStore, ctx?: BasicContext<BasicConfig>) => PlanningStore
|
|
146
|
+
/** The first plugin answering a code wins. */
|
|
147
|
+
mintCode: (
|
|
148
|
+
draft: WorkcardDraft, taken: (code: string) => Promise<boolean>, contextOf: (plugin: PlanningPlugin) => PlanningExecContext
|
|
149
|
+
) => Promise<string | undefined>
|
|
150
|
+
/** The `before` chain; answers the execution the chain left. */
|
|
151
|
+
before: (
|
|
152
|
+
exec: TransitionExecution, contextOf: (plugin: PlanningPlugin) => PlanningExecContext
|
|
153
|
+
) => Promise<TransitionExecution>
|
|
154
|
+
/** The `after` chain; a failing hook is logged and the chain goes on. */
|
|
155
|
+
after: (event: CommitEvent, contextOf: (plugin: PlanningPlugin) => PlanningHookContext) => Promise<void>
|
|
156
|
+
/** Bumps on every `use` — what a cached composite store is keyed by. */
|
|
157
|
+
version: () => number
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface PlanningServiceApi extends Pick<PlanningService, 'use' | 'plugins' | 'schemas' | 'store' | 'for' | 'committed'> {}
|
|
161
|
+
|
|
162
|
+
/** What the facade and the executor reach the service through. */
|
|
163
|
+
export interface PlanningRuntime {
|
|
164
|
+
service: () => PlanningService
|
|
165
|
+
registry: PluginRegistry
|
|
166
|
+
options: PlanningServiceOptions
|
|
167
|
+
context: () => BasicContext<BasicConfig> | undefined
|
|
168
|
+
/** The composite store reads and commits go through. */
|
|
169
|
+
reader: () => PlanningStore
|
|
170
|
+
/** Every distinct store: the default one first, then each plugin's. */
|
|
171
|
+
stores: () => PlanningStore[]
|
|
172
|
+
/** The default store's data-defined schema port, when it has one. */
|
|
173
|
+
schemaStore: () => SchemaStore | undefined
|
|
174
|
+
/**
|
|
175
|
+
* The registry a card of this organization (and project) resolves through. Without a schema
|
|
176
|
+
* port it is the service's code registry itself — the very same object.
|
|
177
|
+
*/
|
|
178
|
+
schemasFor: (entityId: string, project?: string) => Promise<PlanningSchemaRegistry>
|
|
179
|
+
/** The resolved layer. @throws {PlanningUnsupported} without a schema port */
|
|
180
|
+
scopedSchemas: (entityId: string, project?: string) => Promise<ScopedSchemaRegistry>
|
|
48
181
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Workcard } from '@owlmeans/planning'
|
|
2
|
+
import type { PlanningAccess, PlanningAccessGrants } from '../../types.js'
|
|
3
|
+
|
|
4
|
+
/** The write rules of one resolved {@link PlanningAccess} — no access at all gates nothing. */
|
|
5
|
+
export interface PlanningAccessModel {
|
|
6
|
+
readonly record: PlanningAccess | undefined
|
|
7
|
+
/**
|
|
8
|
+
* Refuse a write the access does not grant. No access, or access without `grants`, gates nothing;
|
|
9
|
+
* a `grants` object refuses every flag it leaves out. `true` grants everything; a list grants the
|
|
10
|
+
* project ids it names — `target` is the project the write is about (`undefined` for the
|
|
11
|
+
* organization's root, which only `true` reaches).
|
|
12
|
+
*
|
|
13
|
+
* @throws {PlanningForbidden}
|
|
14
|
+
*/
|
|
15
|
+
assertGranted: (grant: keyof PlanningAccessGrants, target?: string) => void
|
|
16
|
+
/**
|
|
17
|
+
* A card the access's `writes` admits — the rule a narrowed read admits, over `writes`: a project
|
|
18
|
+
* it names, or a card whose `parents` name one. A specification is admitted through its parent card
|
|
19
|
+
* by the caller. No `writes` admits everything.
|
|
20
|
+
*/
|
|
21
|
+
writableIn: (card: Pick<Workcard, 'id' | 'parents'>) => boolean
|
|
22
|
+
/**
|
|
23
|
+
* Refuse a write into a project the access's `writes` leaves out. No `writes` refuses nothing;
|
|
24
|
+
* `target` is the project the write is in (`undefined` for the organization's root, which a set
|
|
25
|
+
* `writes` never admits).
|
|
26
|
+
*
|
|
27
|
+
* @throws {PlanningForbidden}
|
|
28
|
+
*/
|
|
29
|
+
assertWrites: (target?: string) => void
|
|
30
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { PlanningForbidden, type Workcard } from '@owlmeans/planning'
|
|
2
|
+
import type { PlanningAccess, PlanningAccessGrants } from '../types.js'
|
|
3
|
+
import type { PlanningAccessModel } from './access/types.js'
|
|
4
|
+
|
|
5
|
+
export const makePlanningAccessModel = (access: PlanningAccess | undefined): PlanningAccessModel => {
|
|
6
|
+
const assertGranted = (grant: keyof PlanningAccessGrants, target?: string): void => {
|
|
7
|
+
if (access?.grants == null) {
|
|
8
|
+
return
|
|
9
|
+
}
|
|
10
|
+
const granted = access.grants[grant]
|
|
11
|
+
if (granted === true || (Array.isArray(granted) && target != null && granted.includes(target))) {
|
|
12
|
+
return
|
|
13
|
+
}
|
|
14
|
+
throw new PlanningForbidden(`${grant}:${target ?? 'root'}`)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const writableIn = (card: Pick<Workcard, 'id' | 'parents'>): boolean => {
|
|
18
|
+
const writes = access?.writes
|
|
19
|
+
if (writes == null) {
|
|
20
|
+
return true
|
|
21
|
+
}
|
|
22
|
+
return (card.id != null && writes.includes(card.id)) || card.parents.some(parent => writes.includes(parent))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const assertWrites = (target?: string): void => {
|
|
26
|
+
if (access?.writes == null || (target != null && access.writes.includes(target))) {
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
throw new PlanningForbidden(`writes:${target ?? 'root'}`)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return { record: access, assertGranted, writableIn, assertWrites }
|
|
33
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { WorkcardNotFound, Workcard, PlanningReply } from '@owlmeans/planning'
|
|
2
|
+
|
|
3
|
+
/** What every handler answers with when a record is not the caller's: absence, never a refusal. */
|
|
4
|
+
export interface GuardHelper {
|
|
5
|
+
notFoundOf: (id: string) => WorkcardNotFound
|
|
6
|
+
/**
|
|
7
|
+
* A record of another entity is absent, not forbidden.
|
|
8
|
+
*
|
|
9
|
+
* @throws {WorkcardNotFound}
|
|
10
|
+
*/
|
|
11
|
+
assertScope: <T extends Pick<Workcard, 'entityId'>>(record: T | null | undefined, scope: { entityId: string }, id: string) => T
|
|
12
|
+
/**
|
|
13
|
+
* Run a handler body so that a scope mismatch leaves as `WorkcardNotFound` — telling "not yours"
|
|
14
|
+
* apart from "does not exist" is what turns an id space into an enumeration oracle.
|
|
15
|
+
*/
|
|
16
|
+
concealed: <T>(run: () => Promise<T>) => Promise<T>
|
|
17
|
+
/** Conceal scope failures and project storage metadata out of a public reply. */
|
|
18
|
+
reply: <T>(run: () => Promise<T>) => Promise<PlanningReply<T>>
|
|
19
|
+
/** Seconds as the wire sends them, clamped to `[0, max]`. */
|
|
20
|
+
clampSeconds: (value: unknown, max: number) => number
|
|
21
|
+
}
|
package/src/utils/guard.ts
CHANGED
|
@@ -1,40 +1,53 @@
|
|
|
1
|
-
import { PlanningScopeMismatch, WorkcardNotFound } from '@owlmeans/planning'
|
|
1
|
+
import { planningReplyHelper, PlanningScopeMismatch, WorkcardNotFound } from '@owlmeans/planning'
|
|
2
2
|
import type { Workcard } from '@owlmeans/planning'
|
|
3
|
+
import type { GuardHelper } from './guard/types.js'
|
|
3
4
|
|
|
4
|
-
export const
|
|
5
|
+
export const createGuardHelper = (): GuardHelper => {
|
|
6
|
+
const notFoundOf = (id: string): WorkcardNotFound => new WorkcardNotFound(id)
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
): T => {
|
|
14
|
-
if (record == null || record.entityId !== scope.entityId) {
|
|
15
|
-
throw notFoundOf(id)
|
|
8
|
+
const assertScope = <T extends Pick<Workcard, 'entityId'>>(
|
|
9
|
+
record: T | null | undefined, scope: { entityId: string }, id: string
|
|
10
|
+
): T => {
|
|
11
|
+
if (record == null || record.entityId !== scope.entityId) {
|
|
12
|
+
throw notFoundOf(id)
|
|
13
|
+
}
|
|
14
|
+
return record
|
|
16
15
|
}
|
|
17
|
-
return record
|
|
18
|
-
}
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|| (error as { type?: string } | null)?.type === PlanningScopeMismatch.typeName) {
|
|
30
|
-
throw new WorkcardNotFound()
|
|
17
|
+
const concealed = async <T>(run: () => Promise<T>): Promise<T> => {
|
|
18
|
+
try {
|
|
19
|
+
return await run()
|
|
20
|
+
} catch (error) {
|
|
21
|
+
if (error instanceof PlanningScopeMismatch
|
|
22
|
+
|| (error as { type?: string } | null)?.type === PlanningScopeMismatch.typeName) {
|
|
23
|
+
throw new WorkcardNotFound()
|
|
24
|
+
}
|
|
25
|
+
throw error
|
|
31
26
|
}
|
|
32
|
-
throw error
|
|
33
27
|
}
|
|
34
|
-
}
|
|
35
28
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
29
|
+
const clampSeconds = (value: unknown, max: number): number => {
|
|
30
|
+
const number = typeof value === 'number' ? value : Number(value ?? 0)
|
|
31
|
+
return Number.isFinite(number) ? Math.min(Math.max(number, 0), max) : 0
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return { notFoundOf, assertScope, concealed, clampSeconds,
|
|
35
|
+
reply: async run => planningReplyHelper.project(await concealed(run)),
|
|
36
|
+
}
|
|
40
37
|
}
|
|
38
|
+
|
|
39
|
+
export const guardHelper = createGuardHelper()
|
|
40
|
+
|
|
41
|
+
/** @deprecated compat:factory-refactor — use `guardHelper.notFoundOf(…)` */
|
|
42
|
+
export const notFoundOf = (id: string): WorkcardNotFound => guardHelper.notFoundOf(id)
|
|
43
|
+
|
|
44
|
+
/** @deprecated compat:factory-refactor — use `guardHelper.assertScope(…)` */
|
|
45
|
+
export const assertScope = <T extends Pick<Workcard, 'entityId'>>(
|
|
46
|
+
record: T | null | undefined, scope: { entityId: string }, id: string
|
|
47
|
+
): T => guardHelper.assertScope<T>(record, scope, id)
|
|
48
|
+
|
|
49
|
+
/** @deprecated compat:factory-refactor — use `guardHelper.concealed(…)` */
|
|
50
|
+
export const concealed = async <T>(run: () => Promise<T>): Promise<T> => await guardHelper.concealed<T>(run)
|
|
51
|
+
|
|
52
|
+
/** @deprecated compat:factory-refactor — use `guardHelper.clampSeconds(…)` */
|
|
53
|
+
export const clampSeconds = (value: unknown, max: number): number => guardHelper.clampSeconds(value, max)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AbstractRequest } from '@owlmeans/entrypoint'
|
|
2
|
+
import type { PlanningFacade, PlanningScope, PlanningService } from '@owlmeans/planning'
|
|
3
|
+
import type { PlanningHandlerOptions } from '../../types.js'
|
|
4
|
+
import type { HandlerScope } from '../types.js'
|
|
5
|
+
|
|
6
|
+
/** The planning service of one context, as a request handler reaches it. */
|
|
7
|
+
export interface PlanningHandlerHelper {
|
|
8
|
+
planningServiceOf: (opts?: Pick<PlanningHandlerOptions, 'service'>) => PlanningService
|
|
9
|
+
/**
|
|
10
|
+
* The facade a handler works through, and the access it was resolved under.
|
|
11
|
+
*
|
|
12
|
+
* Without `opts.access` it is exactly the request's scope plus what `opts.scope` derives. With it,
|
|
13
|
+
* the resolver names the organization and the visible projects; a throw from the resolver is the
|
|
14
|
+
* request's error.
|
|
15
|
+
*/
|
|
16
|
+
handlerScopeOf: (req: AbstractRequest, opts?: PlanningHandlerOptions) => Promise<HandlerScope>
|
|
17
|
+
/** The facade a handler works through: the request's scope plus what `opts.scope` derives. */
|
|
18
|
+
handlerFacade: (req: AbstractRequest, opts?: PlanningHandlerOptions) => Promise<PlanningFacade>
|
|
19
|
+
/**
|
|
20
|
+
* The facade a hand-written handler works through: the request's entity and subject, plus `extra`
|
|
21
|
+
* (a `channel`) — never an entity from the request body.
|
|
22
|
+
*
|
|
23
|
+
* @throws {AuthorizationError} when the request carries no organization
|
|
24
|
+
*/
|
|
25
|
+
planningFor: (
|
|
26
|
+
req: AbstractRequest, extra?: Partial<PlanningScope>, opts?: Pick<PlanningHandlerOptions, 'service'>
|
|
27
|
+
) => PlanningFacade
|
|
28
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { memoHelper, type BasicConfig, type BasicContext } from '@owlmeans/context'
|
|
2
|
+
import type { AbstractRequest } from '@owlmeans/entrypoint'
|
|
3
|
+
import { PLANNING_SERVICE, type PlanningFacade, type PlanningScope, type PlanningService } from '@owlmeans/planning'
|
|
4
|
+
import type { PlanningHandlerOptions, PlanningHostService } from '../types.js'
|
|
5
|
+
import type { PlanningHandlerHelper } from './handler/types.js'
|
|
6
|
+
import { makeRequestScope } from './request.js'
|
|
7
|
+
import type { HandlerScope } from './types.js'
|
|
8
|
+
|
|
9
|
+
export const makePlanningHandlerHelper = (ctx: BasicContext<BasicConfig>): PlanningHandlerHelper => {
|
|
10
|
+
const planningServiceOf = (opts?: Pick<PlanningHandlerOptions, 'service'>): PlanningService =>
|
|
11
|
+
ctx.service<PlanningHostService>(opts?.service ?? PLANNING_SERVICE)
|
|
12
|
+
|
|
13
|
+
const handlerScopeOf = async (req: AbstractRequest, opts?: PlanningHandlerOptions): Promise<HandlerScope> => {
|
|
14
|
+
const planningService = planningServiceOf(opts)
|
|
15
|
+
const extra = await opts?.scope?.(req, ctx)
|
|
16
|
+
if (opts?.access == null) {
|
|
17
|
+
return { facade: planningService.for(makeRequestScope(req).scopeOf(extra ?? undefined)) }
|
|
18
|
+
}
|
|
19
|
+
const access = await opts.access(req, ctx)
|
|
20
|
+
|
|
21
|
+
return { facade: planningService.for(makeRequestScope(req).accessScopeOf(access, extra ?? undefined)), access }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const handlerFacade = async (req: AbstractRequest, opts?: PlanningHandlerOptions): Promise<PlanningFacade> =>
|
|
25
|
+
(await handlerScopeOf(req, opts)).facade
|
|
26
|
+
|
|
27
|
+
const planningFor = (
|
|
28
|
+
req: AbstractRequest, extra?: Partial<PlanningScope>, opts?: Pick<PlanningHandlerOptions, 'service'>
|
|
29
|
+
): PlanningFacade => planningServiceOf(opts).for(makeRequestScope(req).scopeOf(extra))
|
|
30
|
+
|
|
31
|
+
return { planningServiceOf, handlerScopeOf, handlerFacade, planningFor }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The handler helper of a context — one per context. */
|
|
35
|
+
export const planningHandlerOf = memoHelper.oncePer(makePlanningHandlerHelper)
|
package/src/utils/index.ts
CHANGED
|
@@ -1,2 +1,11 @@
|
|
|
1
|
+
export type * from './types.js'
|
|
2
|
+
export type * from './access/types.js'
|
|
3
|
+
export type * from './guard/types.js'
|
|
4
|
+
export type * from './handler/types.js'
|
|
5
|
+
export type * from './request/types.js'
|
|
6
|
+
|
|
7
|
+
export * from './access.js'
|
|
1
8
|
export * from './guard.js'
|
|
9
|
+
export * from './handler.js'
|
|
10
|
+
export * from './request.js'
|
|
2
11
|
export * from './scope.js'
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { PlanningScope, TransitionActor } from '@owlmeans/planning'
|
|
2
|
+
import type { PlanningAccess } from '../../types.js'
|
|
3
|
+
|
|
4
|
+
/** One request's identity as planning reads it — built at the top of a handler, never stored. */
|
|
5
|
+
export interface RequestScope {
|
|
6
|
+
/** The authenticated subject of a request, as a transition names it. */
|
|
7
|
+
actorOf: () => TransitionActor
|
|
8
|
+
/**
|
|
9
|
+
* The scope a request reads and writes as.
|
|
10
|
+
*
|
|
11
|
+
* `entityId` is `requireEntityKey(req)` — the resolved organization id, never a value from the
|
|
12
|
+
* body or the query — and `extra` cannot replace it; `extra` adds what the deployment derives
|
|
13
|
+
* (a `channel`, a `service`). The actor is the authenticated subject plus that channel.
|
|
14
|
+
*
|
|
15
|
+
* @throws {AuthorizationError} when the request carries no organization
|
|
16
|
+
*/
|
|
17
|
+
scopeOf: (extra?: Partial<PlanningScope>) => PlanningScope
|
|
18
|
+
/**
|
|
19
|
+
* The scope of a request under a resolved {@link PlanningAccess}: the organization is the
|
|
20
|
+
* resolver's — never the token's, never `extra`'s — and `projects` narrows it.
|
|
21
|
+
*/
|
|
22
|
+
accessScopeOf: (access: PlanningAccess, extra?: Partial<PlanningScope>) => PlanningScope
|
|
23
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
|
|
2
|
+
import type { AbstractRequest } from '@owlmeans/entrypoint'
|
|
3
|
+
import type { PlanningScope, TransitionActor } from '@owlmeans/planning'
|
|
4
|
+
import type { PlanningAccess } from '../types.js'
|
|
5
|
+
import type { RequestScope } from './request/types.js'
|
|
6
|
+
import { makeEntityScope } from '@owlmeans/auth-common'
|
|
7
|
+
|
|
8
|
+
const clean = <T extends object>(record: T): T =>
|
|
9
|
+
Object.fromEntries(Object.entries(record).filter(([, value]) => value != null && value !== '')) as T
|
|
10
|
+
|
|
11
|
+
export const makeRequestScope = (req: AbstractRequest): RequestScope => {
|
|
12
|
+
const actorOf = (): TransitionActor => clean({
|
|
13
|
+
profileId: req.auth?.profileId,
|
|
14
|
+
userId: req.auth?.userId,
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const scopeWith = (entityId: string, extra?: Partial<PlanningScope>): PlanningScope => {
|
|
18
|
+
const subject = actorOf()
|
|
19
|
+
const channel = extra?.channel ?? extra?.actor?.channel
|
|
20
|
+
|
|
21
|
+
return clean({
|
|
22
|
+
...extra,
|
|
23
|
+
...subject,
|
|
24
|
+
entityId,
|
|
25
|
+
channel,
|
|
26
|
+
actor: clean({ ...extra?.actor, ...subject, channel }),
|
|
27
|
+
}) as PlanningScope
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const scopeOf = (extra?: Partial<PlanningScope>): PlanningScope => scopeWith(makeEntityScope(req).requireEntityKey(), extra)
|
|
31
|
+
|
|
32
|
+
const accessScopeOf = (access: PlanningAccess, extra?: Partial<PlanningScope>): PlanningScope => {
|
|
33
|
+
const { projects: _projects, entityId: _entityId, ...rest } = extra ?? {}
|
|
34
|
+
const scope = scopeWith(access.entityId, rest)
|
|
35
|
+
|
|
36
|
+
return access.projects != null ? { ...scope, projects: [...access.projects] } : scope
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return { actorOf, scopeOf, accessScopeOf }
|
|
40
|
+
}
|
package/src/utils/scope.ts
CHANGED
|
@@ -1,51 +1,48 @@
|
|
|
1
|
-
import { requireEntityKey } from '@owlmeans/auth-common'
|
|
2
1
|
import type { BasicConfig, BasicContext } from '@owlmeans/context'
|
|
3
2
|
import type { AbstractRequest } from '@owlmeans/entrypoint'
|
|
4
|
-
import {
|
|
5
|
-
import type {
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
/** The authenticated subject of a request, as a transition names it. */
|
|
12
|
-
export const actorOf = (req: AbstractRequest): TransitionActor => clean({
|
|
13
|
-
profileId: req.auth?.profileId,
|
|
14
|
-
userId: req.auth?.userId,
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* The scope a request reads and writes as.
|
|
19
|
-
*
|
|
20
|
-
* `entityId` is `requireEntityKey(req)` — the resolved organization id, never a value from the
|
|
21
|
-
* body or the query — and `extra` cannot replace it; `extra` adds what the deployment derives
|
|
22
|
-
* (a `channel`, a `service`). The actor is the authenticated subject plus that channel.
|
|
23
|
-
*
|
|
24
|
-
* @throws {AuthorizationError} when the request carries no organization
|
|
25
|
-
*/
|
|
26
|
-
export const scopeOf = (req: AbstractRequest, extra?: Partial<PlanningScope>): PlanningScope => {
|
|
27
|
-
const entityId = requireEntityKey(req)
|
|
28
|
-
const subject = actorOf(req)
|
|
29
|
-
const channel = extra?.channel ?? extra?.actor?.channel
|
|
30
|
-
|
|
31
|
-
return clean({
|
|
32
|
-
...extra,
|
|
33
|
-
...subject,
|
|
34
|
-
entityId,
|
|
35
|
-
channel,
|
|
36
|
-
actor: clean({ ...extra?.actor, ...subject, channel }),
|
|
37
|
-
}) as PlanningScope
|
|
38
|
-
}
|
|
3
|
+
import type { PlanningFacade, PlanningScope, PlanningService, TransitionActor, Workcard } from '@owlmeans/planning'
|
|
4
|
+
import type { PlanningAccess, PlanningAccessGrants, PlanningHandlerOptions } from '../types.js'
|
|
5
|
+
import { makePlanningAccessModel } from './access.js'
|
|
6
|
+
import { planningHandlerOf } from './handler.js'
|
|
7
|
+
import { makeRequestScope } from './request.js'
|
|
8
|
+
import type { HandlerScope } from './types.js'
|
|
39
9
|
|
|
10
|
+
/** @deprecated compat:factory-refactor — use `makeRequestScope(req).actorOf()` */
|
|
11
|
+
export const actorOf = (req: AbstractRequest): TransitionActor => makeRequestScope(req).actorOf()
|
|
12
|
+
|
|
13
|
+
/** @deprecated compat:factory-refactor — use `makeRequestScope(req).scopeOf(…)` */
|
|
14
|
+
export const scopeOf = (req: AbstractRequest, extra?: Partial<PlanningScope>): PlanningScope =>
|
|
15
|
+
makeRequestScope(req).scopeOf(extra)
|
|
16
|
+
|
|
17
|
+
/** @deprecated compat:factory-refactor — use `makeRequestScope(req).accessScopeOf(…)` */
|
|
18
|
+
export const accessScopeOf = (
|
|
19
|
+
req: AbstractRequest, access: PlanningAccess, extra?: Partial<PlanningScope>
|
|
20
|
+
): PlanningScope => makeRequestScope(req).accessScopeOf(access, extra)
|
|
21
|
+
|
|
22
|
+
/** @deprecated compat:factory-refactor — use `planningHandlerOf(ctx).planningServiceOf(…)` */
|
|
40
23
|
export const planningServiceOf = (
|
|
41
24
|
ctx: BasicContext<BasicConfig>, opts?: Pick<PlanningHandlerOptions, 'service'>
|
|
42
|
-
): PlanningService => ctx.
|
|
25
|
+
): PlanningService => planningHandlerOf(ctx).planningServiceOf(opts)
|
|
43
26
|
|
|
44
|
-
/**
|
|
27
|
+
/** @deprecated compat:factory-refactor — use `planningHandlerOf(ctx).handlerScopeOf(…)` */
|
|
28
|
+
export const handlerScopeOf = async (
|
|
29
|
+
ctx: BasicContext<BasicConfig>, req: AbstractRequest, opts?: PlanningHandlerOptions
|
|
30
|
+
): Promise<HandlerScope> => await planningHandlerOf(ctx).handlerScopeOf(req, opts)
|
|
31
|
+
|
|
32
|
+
/** @deprecated compat:factory-refactor — use `planningHandlerOf(ctx).handlerFacade(…)` */
|
|
45
33
|
export const handlerFacade = async (
|
|
46
34
|
ctx: BasicContext<BasicConfig>, req: AbstractRequest, opts?: PlanningHandlerOptions
|
|
47
|
-
): Promise<PlanningFacade> =>
|
|
48
|
-
|
|
35
|
+
): Promise<PlanningFacade> => await planningHandlerOf(ctx).handlerFacade(req, opts)
|
|
36
|
+
|
|
37
|
+
/** @deprecated compat:factory-refactor — use `makePlanningAccessModel(access).assertGranted(…)` */
|
|
38
|
+
export const assertGranted = (
|
|
39
|
+
access: PlanningAccess | undefined, grant: keyof PlanningAccessGrants, target?: string
|
|
40
|
+
): void => makePlanningAccessModel(access).assertGranted(grant, target)
|
|
41
|
+
|
|
42
|
+
/** @deprecated compat:factory-refactor — use `makePlanningAccessModel(access).writableIn(…)` */
|
|
43
|
+
export const writableIn = (access: PlanningAccess | undefined, card: Pick<Workcard, 'id' | 'parents'>): boolean =>
|
|
44
|
+
makePlanningAccessModel(access).writableIn(card)
|
|
49
45
|
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
/** @deprecated compat:factory-refactor — use `makePlanningAccessModel(access).assertWrites(…)` */
|
|
47
|
+
export const assertWrites = (access: PlanningAccess | undefined, target?: string): void =>
|
|
48
|
+
makePlanningAccessModel(access).assertWrites(target)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PlanningFacade } from '@owlmeans/planning'
|
|
2
|
+
import type { PlanningAccess } from '../types.js'
|
|
3
|
+
|
|
4
|
+
export interface HandlerScope {
|
|
5
|
+
facade: PlanningFacade
|
|
6
|
+
/** The resolver's answer, when the handler options carry one. */
|
|
7
|
+
access?: PlanningAccess
|
|
8
|
+
}
|