@owlmeans/server-planning 0.1.18-rc.21 → 0.1.18-rc.22
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 +24 -17
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/server-planning/SKILL.md +28 -28
- 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 +31 -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 +8 -28
- package/build/actions/execute.d.ts.map +1 -1
- package/build/actions/execute.js +17 -101
- 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 +79 -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/{specs.d.ts → list-specification-revisions.d.ts} +1 -3
- 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/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 +5 -3
- package/build/actions/types.d.ts.map +1 -1
- package/build/actions/watch.d.ts +2 -2
- package/build/actions/watch.d.ts.map +1 -1
- package/build/actions/watch.js +2 -2
- 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 +8 -13
- package/build/conformance/assert.d.ts.map +1 -1
- package/build/conformance/assert.js +41 -43
- package/build/conformance/assert.js.map +1 -1
- package/build/conformance/cases.d.ts.map +1 -1
- package/build/conformance/cases.js +86 -67
- package/build/conformance/cases.js.map +1 -1
- 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 +3 -1
- package/build/conformance/consts.d.ts.map +1 -1
- package/build/conformance/consts.js +84 -1
- package/build/conformance/consts.js.map +1 -1
- 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 +8 -8
- package/build/conformance/fixtures.d.ts.map +1 -1
- package/build/conformance/fixtures.js +24 -107
- package/build/conformance/fixtures.js.map +1 -1
- package/build/conformance/index.d.ts +3 -0
- package/build/conformance/index.d.ts.map +1 -1
- package/build/conformance/index.js +1 -0
- package/build/conformance/index.js.map +1 -1
- package/build/definitions.d.ts.map +1 -1
- package/build/definitions.js +9 -9
- package/build/definitions.js.map +1 -1
- 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/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 +6 -25
- package/build/executor/creator.d.ts.map +1 -1
- package/build/executor/creator.js +26 -41
- package/build/executor/creator.js.map +1 -1
- 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 +4 -41
- package/build/executor/resolve.d.ts.map +1 -1
- package/build/executor/resolve.js +142 -157
- package/build/executor/resolve.js.map +1 -1
- package/build/executor/types.d.ts +0 -2
- package/build/executor/types.d.ts.map +1 -1
- package/build/executor/validate.d.ts +1 -1
- package/build/executor/validate.d.ts.map +1 -1
- package/build/executor/validate.js +16 -15
- package/build/executor/validate.js.map +1 -1
- package/build/executor.d.ts +4 -3
- package/build/executor.d.ts.map +1 -1
- package/build/executor.js +18 -17
- package/build/executor.js.map +1 -1
- package/build/facade.d.ts.map +1 -1
- package/build/facade.js +16 -16
- package/build/facade.js.map +1 -1
- package/build/helper.d.ts +1 -6
- package/build/helper.d.ts.map +1 -1
- package/build/helper.js +3 -8
- package/build/helper.js.map +1 -1
- package/build/projection.d.ts +2 -0
- package/build/projection.d.ts.map +1 -1
- package/build/projection.js +6 -4
- package/build/projection.js.map +1 -1
- 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 +1 -4
- package/build/queue/index.d.ts.map +1 -1
- package/build/queue/index.js +1 -4
- package/build/queue/index.js.map +1 -1
- package/build/store/composite.d.ts.map +1 -1
- package/build/store/composite.js +2 -2
- package/build/store/composite.js.map +1 -1
- 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 +7 -30
- package/build/store/fold.d.ts.map +1 -1
- package/build/store/fold.js +185 -203
- 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.map +1 -1
- package/build/store/memory.js +0 -0
- package/build/store/memory.js.map +1 -1
- package/build/store/types.local.d.ts +3 -1
- package/build/store/types.local.d.ts.map +1 -1
- package/build/types.d.ts +2 -1
- package/build/types.d.ts.map +1 -1
- 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 +21 -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 +33 -31
- 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 -1
- 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 +10 -43
- package/build/utils/scope.d.ts.map +1 -1
- package/build/utils/scope.js +21 -99
- package/build/utils/scope.js.map +1 -1
- package/package.json +18 -18
- package/src/actions/apply-schema-request.ts +35 -0
- package/src/actions/commit.ts +7 -5
- package/src/actions/define-schemas.ts +34 -0
- package/src/actions/execute.ts +30 -115
- package/src/actions/execution/types.ts +34 -0
- package/src/actions/execution.ts +96 -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/{specs.ts → list-specification-revisions.ts} +4 -11
- package/src/actions/summarize-cards.ts +15 -0
- package/src/actions/transitions.ts +6 -4
- package/src/actions/types.ts +4 -3
- package/src/actions/watch.ts +4 -3
- package/src/conformance/assert/types.ts +16 -0
- package/src/conformance/assert.ts +49 -41
- package/src/conformance/cases.ts +88 -67
- package/src/conformance/clock.ts +11 -0
- package/src/conformance/consts.ts +87 -1
- package/src/conformance/errors.ts +7 -0
- package/src/conformance/fixtures/types.ts +11 -0
- package/src/conformance/fixtures.ts +32 -107
- package/src/conformance/index.ts +3 -0
- package/src/definitions.ts +9 -9
- package/src/executor/changes/types.ts +38 -0
- package/src/executor/changes.ts +48 -63
- package/src/executor/code/types.ts +23 -0
- package/src/executor/code.ts +60 -61
- package/src/executor/creator/types.ts +33 -0
- package/src/executor/creator.ts +33 -41
- package/src/executor/resolve/types.ts +30 -0
- package/src/executor/resolve.ts +153 -167
- package/src/executor/types.ts +0 -3
- package/src/executor/validate.ts +17 -16
- package/src/executor.ts +20 -17
- package/src/facade.ts +16 -16
- package/src/helper.ts +3 -8
- package/src/projection.ts +8 -4
- package/src/queue/declare.ts +7 -0
- package/src/queue/index.ts +1 -8
- package/src/store/composite.ts +2 -2
- package/src/store/fold/types.ts +44 -0
- package/src/store/fold.ts +209 -212
- package/src/store/index.ts +1 -0
- package/src/store/memory.ts +0 -0
- package/src/store/types.local.ts +1 -1
- package/src/types.ts +1 -1
- package/src/utils/access/types.ts +30 -0
- package/src/utils/access.ts +33 -0
- package/src/utils/guard/types.ts +19 -0
- package/src/utils/guard.ts +41 -30
- package/src/utils/handler/types.ts +28 -0
- package/src/utils/handler.ts +35 -0
- package/src/utils/index.ts +9 -1
- package/src/utils/request/types.ts +23 -0
- package/src/utils/request.ts +40 -0
- package/src/utils/scope.ts +25 -108
- package/tests/creator.spec.ts +18 -18
- package/build/actions/cards.d.ts +0 -14
- 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 -28
- package/build/actions/schemas.d.ts.map +0 -1
- package/build/actions/schemas.js +0 -69
- package/build/actions/schemas.js.map +0 -1
- 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 -48
- package/src/actions/schemas.ts +0 -87
package/src/definitions.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { cardHelper, PlanningSchemaKind, PlanningUnsupported, SchemaConflict, SchemaInUse, scopedSchemaHelper, UnknownStatusFlow, UnknownWorkcardType, WorkcardNotFound, type PlanningDefinitions, type PlanningFacade, type PlanningSchemaRegistry, type SchemaDeclarations, type SchemaStore, type SchemaWriteOptions, type ScopedSchemaRecord, type ScopedSchemaRegistry, type StatusFlowSchema, type TransitionExecution, type Unsubscribe, type WorkcardTypeSchema } from '@owlmeans/planning'
|
|
2
2
|
import { DEFAULT_SCHEMA_VIEWS } from './consts.js'
|
|
3
|
-
import {
|
|
3
|
+
import { changesUtils } from './executor/changes.js'
|
|
4
4
|
import type { PlanningRuntime, SchemaViews, SchemaViewsOptions } from './types.js'
|
|
5
5
|
import type { Planned } from './types.local.js'
|
|
6
6
|
|
|
@@ -52,7 +52,7 @@ export const makeSchemaViews = (opts: SchemaViewsOptions): SchemaViews => {
|
|
|
52
52
|
...await store.list({ entityId, project: null }),
|
|
53
53
|
...(project != null ? await store.list({ entityId, project }) : []),
|
|
54
54
|
]
|
|
55
|
-
const view = scopedRegistryOf(resolveScopedBundle(opts.code().bundle(), records, { entityId, project, revision }))
|
|
55
|
+
const view = scopedSchemaHelper.scopedRegistryOf(scopedSchemaHelper.resolveScopedBundle(opts.code().bundle(), records, { entityId, project, revision }))
|
|
56
56
|
cache.delete(key)
|
|
57
57
|
cache.set(key, { revision, version, view })
|
|
58
58
|
while (cache.size > limit) {
|
|
@@ -106,7 +106,7 @@ export const makeDefinitions = (runtime: PlanningRuntime, facade: PlanningFacade
|
|
|
106
106
|
return
|
|
107
107
|
}
|
|
108
108
|
const card = await facade.cards.load(project)
|
|
109
|
-
if (card == null || !isProject(card)) {
|
|
109
|
+
if (card == null || !cardHelper.isProject(card)) {
|
|
110
110
|
throw new WorkcardNotFound(project)
|
|
111
111
|
}
|
|
112
112
|
}
|
|
@@ -124,11 +124,11 @@ export const makeDefinitions = (runtime: PlanningRuntime, facade: PlanningFacade
|
|
|
124
124
|
const flows: Pick<PlanningSchemaRegistry, 'flow'> = { flow: id => pending.get(id) ?? layer.flow(id) }
|
|
125
125
|
|
|
126
126
|
for (const entry of plan) {
|
|
127
|
-
assertOverridable(code, entry.kind, entry.key)
|
|
127
|
+
scopedSchemaHelper.assertOverridable(code, entry.kind, entry.key)
|
|
128
128
|
if (entry.kind === PlanningSchemaKind.Flow) {
|
|
129
|
-
assertFlowSchema(entry.definition as StatusFlowSchema)
|
|
129
|
+
scopedSchemaHelper.assertFlowSchema(entry.definition as StatusFlowSchema)
|
|
130
130
|
} else {
|
|
131
|
-
assertTypeSchema(entry.definition as WorkcardTypeSchema, flows)
|
|
131
|
+
scopedSchemaHelper.assertTypeSchema(entry.definition as WorkcardTypeSchema, flows)
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
}
|
|
@@ -137,7 +137,7 @@ export const makeDefinitions = (runtime: PlanningRuntime, facade: PlanningFacade
|
|
|
137
137
|
entry: Planned, version: number, project: string | undefined, current?: ScopedSchemaRecord, retired?: boolean
|
|
138
138
|
): ScopedSchemaRecord => {
|
|
139
139
|
const at = now()
|
|
140
|
-
const by = actorOf(scope, {} as TransitionExecution)
|
|
140
|
+
const by = changesUtils.actorOf(scope, {} as TransitionExecution)
|
|
141
141
|
return Object.fromEntries(Object.entries({
|
|
142
142
|
entityId,
|
|
143
143
|
project,
|
|
@@ -235,7 +235,7 @@ export const makeDefinitions = (runtime: PlanningRuntime, facade: PlanningFacade
|
|
|
235
235
|
? [opts.project]
|
|
236
236
|
: [undefined, ...new Set(all.map(record => record.project).filter((id): id is string => id != null))]
|
|
237
237
|
for (const project of projects) {
|
|
238
|
-
const user = flowInUse(resolveScopedBundle(code, all, { entityId, project }), key)
|
|
238
|
+
const user = scopedSchemaHelper.flowInUse(scopedSchemaHelper.resolveScopedBundle(code, all, { entityId, project }), key)
|
|
239
239
|
if (user != null) {
|
|
240
240
|
throw new SchemaInUse(`flow:${key}:type:${user}${project != null ? `:project:${project}` : ''}`)
|
|
241
241
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ChangeSet, PlanningSchemaRegistry, PlanningScope, Transition, TransitionActor, TransitionExecution,
|
|
3
|
+
} from '@owlmeans/planning'
|
|
4
|
+
import type { Resolved } from '../types.js'
|
|
5
|
+
|
|
6
|
+
/** What step 11 builds the appended transition from. */
|
|
7
|
+
export interface AppendParams {
|
|
8
|
+
exec: TransitionExecution
|
|
9
|
+
resolved: Resolved
|
|
10
|
+
scope: PlanningScope
|
|
11
|
+
set: ChangeSet
|
|
12
|
+
cardId: string
|
|
13
|
+
seq: number
|
|
14
|
+
project?: string
|
|
15
|
+
at: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** The record an execution appends: its changes, its actor and the transition itself. */
|
|
19
|
+
export interface ChangesUtils {
|
|
20
|
+
/**
|
|
21
|
+
* Step 9: the recorded `changes`/`unset`, and whether an update wrote nothing.
|
|
22
|
+
*
|
|
23
|
+
* @throws {IllegalTransition | PlanningError}
|
|
24
|
+
*/
|
|
25
|
+
changeSetOf: (
|
|
26
|
+
exec: TransitionExecution, resolved: Resolved, schemas: PlanningSchemaRegistry, at: string
|
|
27
|
+
) => { set: ChangeSet, empty: boolean }
|
|
28
|
+
/**
|
|
29
|
+
* Who wrote it — the SCOPE's identity, never the execution's.
|
|
30
|
+
*
|
|
31
|
+
* `profileId`, `userId`, `service` and `channel` come only from the scope (the server builds it
|
|
32
|
+
* from the authenticated request); the descriptive `agent`/`runId` an in-process caller passes on
|
|
33
|
+
* the execution are kept unless the scope's own actor names them.
|
|
34
|
+
*/
|
|
35
|
+
actorOf: (scope: PlanningScope, exec: TransitionExecution) => TransitionActor
|
|
36
|
+
/** Step 11's record: the event as it is appended, `pending`. */
|
|
37
|
+
transitionOf: (params: AppendParams) => Transition
|
|
38
|
+
}
|
package/src/executor/changes.ts
CHANGED
|
@@ -1,74 +1,59 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { changesHelper, CommitState, TransitionAction, type ChangeSet, type PlanningSchemaRegistry, type PlanningScope, type RelationshipDraft, type Transition, type TransitionActor, type TransitionExecution, type WorkcardDraft } from '@owlmeans/planning'
|
|
2
|
+
import type { ChangesUtils, AppendParams } from './changes/types.js'
|
|
2
3
|
import type { Resolved } from './types.js'
|
|
3
4
|
|
|
4
5
|
const clean = <T extends object>(record: T): T =>
|
|
5
6
|
Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined)) as T
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
exec: TransitionExecution, resolved: Resolved, schemas: PlanningSchemaRegistry, at: string
|
|
14
|
-
): { set: ChangeSet, empty: boolean } => {
|
|
15
|
-
const set = computeChanges(resolved.card, exec, resolved.type, schemas, at, { slot: resolved.slot })
|
|
16
|
-
const empty = exec.action === TransitionAction.Update && isEmptyChange(set)
|
|
8
|
+
export const createChangesUtils = (): ChangesUtils => {
|
|
9
|
+
const changeSetOf = (
|
|
10
|
+
exec: TransitionExecution, resolved: Resolved, schemas: PlanningSchemaRegistry, at: string
|
|
11
|
+
): { set: ChangeSet, empty: boolean } => {
|
|
12
|
+
const set = changesHelper.computeChanges(resolved.card, exec, resolved.type, schemas, at, { slot: resolved.slot })
|
|
13
|
+
const empty = exec.action === TransitionAction.Update && changesHelper.isEmptyChange(set)
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
}
|
|
15
|
+
return { set, empty }
|
|
16
|
+
}
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
export const actorOf = (scope: PlanningScope, exec: TransitionExecution): TransitionActor => clean({
|
|
29
|
-
agent: exec.actor?.agent,
|
|
30
|
-
runId: exec.actor?.runId,
|
|
31
|
-
...clean({ profileId: scope.profileId, userId: scope.userId, service: scope.service }),
|
|
32
|
-
...clean(scope.actor ?? {}),
|
|
33
|
-
channel: scope.channel ?? scope.actor?.channel,
|
|
34
|
-
})
|
|
18
|
+
const actorOf = (scope: PlanningScope, exec: TransitionExecution): TransitionActor => clean({
|
|
19
|
+
agent: exec.actor?.agent,
|
|
20
|
+
runId: exec.actor?.runId,
|
|
21
|
+
...clean({ profileId: scope.profileId, userId: scope.userId, service: scope.service }),
|
|
22
|
+
...clean(scope.actor ?? {}),
|
|
23
|
+
channel: scope.channel ?? scope.actor?.channel,
|
|
24
|
+
})
|
|
35
25
|
|
|
36
|
-
const withFrom = (link: RelationshipDraft, card: string): RelationshipDraft => ({ ...link, from: link.from ?? card })
|
|
26
|
+
const withFrom = (link: RelationshipDraft, card: string): RelationshipDraft => ({ ...link, from: link.from ?? card })
|
|
37
27
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
resolved: Resolved
|
|
42
|
-
scope: PlanningScope
|
|
43
|
-
set: ChangeSet
|
|
44
|
-
cardId: string
|
|
45
|
-
seq: number
|
|
46
|
-
project?: string
|
|
47
|
-
at: string
|
|
48
|
-
}): Transition => {
|
|
49
|
-
const { exec, resolved, scope, set, cardId, seq, project, at } = params
|
|
50
|
-
const kind = resolved.create ? (exec.card as WorkcardDraft).kind : resolved.card!.kind
|
|
28
|
+
const transitionOf = (params: AppendParams): Transition => {
|
|
29
|
+
const { exec, resolved, scope, set, cardId, seq, project, at } = params
|
|
30
|
+
const kind = resolved.create ? (exec.card as WorkcardDraft).kind : resolved.card!.kind
|
|
51
31
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
32
|
+
return clean({
|
|
33
|
+
entityId: scope.entityId,
|
|
34
|
+
card: cardId,
|
|
35
|
+
kind,
|
|
36
|
+
type: resolved.type.type,
|
|
37
|
+
project,
|
|
38
|
+
seq,
|
|
39
|
+
action: exec.action,
|
|
40
|
+
flow: set.flow,
|
|
41
|
+
transition: exec.action === TransitionAction.Transit ? exec.transition : undefined,
|
|
42
|
+
from: set.from,
|
|
43
|
+
to: set.to,
|
|
44
|
+
changes: set.changes,
|
|
45
|
+
unset: set.unset.length > 0 ? set.unset : undefined,
|
|
46
|
+
link: exec.link != null && exec.action !== TransitionAction.Create ? withFrom(exec.link, cardId) : undefined,
|
|
47
|
+
links: resolved.create && (exec.links?.length ?? 0) > 0 ? exec.links!.map(link => withFrom(link, cardId)) : undefined,
|
|
48
|
+
actor: actorOf(scope, exec),
|
|
49
|
+
cause: exec.cause,
|
|
50
|
+
key: exec.key,
|
|
51
|
+
at,
|
|
52
|
+
commit: { state: CommitState.Pending },
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return { changeSetOf, actorOf, transitionOf }
|
|
74
57
|
}
|
|
58
|
+
|
|
59
|
+
export const changesUtils = createChangesUtils()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { PlanningExecContext, PlanningPlugin, PlanningScope, TransitionExecution } from '@owlmeans/planning'
|
|
2
|
+
import type { PluginRegistry } from '../../types.js'
|
|
3
|
+
import type { Resolved } from '../types.js'
|
|
4
|
+
|
|
5
|
+
/** Step 8 of the write path: the code a card gets, unique within its policy's scope. */
|
|
6
|
+
export interface CodeUtils {
|
|
7
|
+
/**
|
|
8
|
+
* Step 8: the code a new card gets, or the check a changed code passes.
|
|
9
|
+
*
|
|
10
|
+
* A caller-supplied code is checked for uniqueness; otherwise the plugins' `mintCode` chain answers
|
|
11
|
+
* (first answer wins, and is checked too); otherwise the type's policy mints one — a slug policy
|
|
12
|
+
* derives it from the title. A type with no policy and no supplied code gets none.
|
|
13
|
+
*
|
|
14
|
+
* @throws {CodeTaken}
|
|
15
|
+
*/
|
|
16
|
+
assignCode: (
|
|
17
|
+
exec: TransitionExecution,
|
|
18
|
+
resolved: Resolved,
|
|
19
|
+
scope: PlanningScope,
|
|
20
|
+
registry: PluginRegistry,
|
|
21
|
+
contextOf: (plugin: PlanningPlugin) => PlanningExecContext
|
|
22
|
+
) => Promise<TransitionExecution>
|
|
23
|
+
}
|
package/src/executor/code.ts
CHANGED
|
@@ -1,77 +1,76 @@
|
|
|
1
|
-
import { CODE_MINT_ATTEMPTS, CodeScope, CodeStyle, CodeTaken,
|
|
1
|
+
import { CODE_MINT_ATTEMPTS, codeHelper, CodeScope, CodeStyle, CodeTaken, TransitionAction, type CodePolicy, type PlanningExecContext, type PlanningPlugin, type PlanningScope, type TransitionExecution, type Workcard, type WorkcardDraft } from '@owlmeans/planning'
|
|
2
2
|
import type { Criteria } from '@owlmeans/resource'
|
|
3
3
|
import type { PluginRegistry } from '../types.js'
|
|
4
|
+
import type { CodeUtils } from './code/types.js'
|
|
4
5
|
import type { Resolved } from './types.js'
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
export const createCodeUtils = (): CodeUtils => {
|
|
8
|
+
/** The criteria a code must be unique within — the policy's scope, `parent` when there is none. */
|
|
9
|
+
const scopeCriteria = (
|
|
10
|
+
scope: PlanningScope, type: string, parent: string | undefined, policy?: CodePolicy
|
|
11
|
+
): Criteria<Workcard> => ({
|
|
12
|
+
entityId: scope.entityId,
|
|
13
|
+
type,
|
|
14
|
+
...((policy?.uniqueWithin ?? CodeScope.Parent) === CodeScope.Parent ? { parent: parent ?? null } : {}),
|
|
15
|
+
}) as Criteria<Workcard>
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
+
/** Is a code taken within the scope the type's policy names (its parent when it names none)? */
|
|
18
|
+
const takenProbe = (resolved: Resolved, scope: PlanningScope, parent: string | undefined): (code: string) => Promise<boolean> => {
|
|
19
|
+
const base = scopeCriteria(scope, resolved.type.type, parent, resolved.type.code)
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
+
return async (code: string): Promise<boolean> =>
|
|
22
|
+
await resolved.store.cards.count({ ...base, code } as Criteria<Workcard>) > 0
|
|
23
|
+
}
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
*/
|
|
31
|
-
export const assignCode = async (
|
|
32
|
-
exec: TransitionExecution,
|
|
33
|
-
resolved: Resolved,
|
|
34
|
-
scope: PlanningScope,
|
|
35
|
-
registry: PluginRegistry,
|
|
36
|
-
contextOf: (plugin: PlanningPlugin) => PlanningExecContext
|
|
37
|
-
): Promise<TransitionExecution> => {
|
|
38
|
-
const policy = resolved.type.code
|
|
25
|
+
const assignCode = async (
|
|
26
|
+
exec: TransitionExecution,
|
|
27
|
+
resolved: Resolved,
|
|
28
|
+
scope: PlanningScope,
|
|
29
|
+
registry: PluginRegistry,
|
|
30
|
+
contextOf: (plugin: PlanningPlugin) => PlanningExecContext
|
|
31
|
+
): Promise<TransitionExecution> => {
|
|
32
|
+
const policy = resolved.type.code
|
|
39
33
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
34
|
+
if (exec.action !== TransitionAction.Create) {
|
|
35
|
+
const code = exec.changes?.code
|
|
36
|
+
if (code != null && code !== resolved.card?.code
|
|
37
|
+
&& await takenProbe(resolved, scope, exec.changes?.parent ?? resolved.card?.parent)(code)) {
|
|
38
|
+
throw new CodeTaken(code)
|
|
39
|
+
}
|
|
40
|
+
return exec
|
|
45
41
|
}
|
|
46
|
-
return exec
|
|
47
|
-
}
|
|
48
42
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
43
|
+
const draft = exec.card as WorkcardDraft
|
|
44
|
+
const taken = takenProbe(resolved, scope, draft.parent)
|
|
45
|
+
if (draft.code != null && draft.code !== '') {
|
|
46
|
+
if (await taken(draft.code)) {
|
|
47
|
+
throw new CodeTaken(draft.code)
|
|
48
|
+
}
|
|
49
|
+
return exec
|
|
50
|
+
}
|
|
51
|
+
if (policy == null) {
|
|
52
|
+
return exec
|
|
54
53
|
}
|
|
55
|
-
return exec
|
|
56
|
-
}
|
|
57
|
-
if (policy == null) {
|
|
58
|
-
return exec
|
|
59
|
-
}
|
|
60
54
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
55
|
+
let code = await registry.mintCode(draft, taken, contextOf)
|
|
56
|
+
if (code != null) {
|
|
57
|
+
if (await taken(code)) {
|
|
58
|
+
throw new CodeTaken(code)
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
const count = policy.style === CodeStyle.Sequential
|
|
62
|
+
? await resolved.store.cards.count(scopeCriteria(scope, draft.type, draft.parent, policy))
|
|
63
|
+
: undefined
|
|
64
|
+
code = await codeHelper.mintCode(policy, taken, CODE_MINT_ATTEMPTS, {
|
|
65
|
+
...(policy.style === CodeStyle.Slug ? { seed: draft.title } : {}),
|
|
66
|
+
...(count != null ? { count } : {}),
|
|
67
|
+
})
|
|
65
68
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
? await resolved.store.cards.count(scopeCriteria(scope, draft.type, draft.parent, policy))
|
|
69
|
-
: undefined
|
|
70
|
-
code = await mintCode(policy, taken, CODE_MINT_ATTEMPTS, {
|
|
71
|
-
...(policy.style === CodeStyle.Slug ? { seed: draft.title } : {}),
|
|
72
|
-
...(count != null ? { count } : {}),
|
|
73
|
-
})
|
|
69
|
+
|
|
70
|
+
return { ...exec, card: { ...draft, code } }
|
|
74
71
|
}
|
|
75
72
|
|
|
76
|
-
return {
|
|
73
|
+
return { assignCode }
|
|
77
74
|
}
|
|
75
|
+
|
|
76
|
+
export const codeUtils = createCodeUtils()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { PlanningScope, TransitionExecution } from '@owlmeans/planning'
|
|
2
|
+
|
|
3
|
+
/** Who owns a card: `createdBy`, stamped from the scope on a create and fixed after it. */
|
|
4
|
+
export interface CreatorHelper {
|
|
5
|
+
/**
|
|
6
|
+
* The subject a create made under `scope` is stamped with as its `createdBy`: the scope's
|
|
7
|
+
* `profileId`, else its `userId`, else the same two of its `actor` — the authenticated subject an
|
|
8
|
+
* HTTP scope carries, in the same format. `undefined` for a scope that names nobody (a service or a
|
|
9
|
+
* system scope).
|
|
10
|
+
*/
|
|
11
|
+
creatorOf: (scope: PlanningScope) => string | undefined
|
|
12
|
+
/**
|
|
13
|
+
* A create's draft with `createdBy` defaulted to {@link CreatorHelper.creatorOf} the scope. A
|
|
14
|
+
* `createdBy` the caller supplied wins; a scope that names nobody leaves the draft as it is;
|
|
15
|
+
* anything but a create is returned untouched. Never mutates `exec`.
|
|
16
|
+
*
|
|
17
|
+
* The executor applies it to every execution, so an in-process facade create and a wire create
|
|
18
|
+
* stamp the same value.
|
|
19
|
+
*/
|
|
20
|
+
withCreator: (exec: TransitionExecution, scope: PlanningScope) => TransitionExecution
|
|
21
|
+
/**
|
|
22
|
+
* Refuse an execution that names `createdBy` in `changes` or `unset`, whatever the action — a
|
|
23
|
+
* create included, whose owner is its draft's. After the create it never moves, so an ownership
|
|
24
|
+
* check (`card.createdBy === profileId`) cannot be bypassed by anyone allowed to update the card.
|
|
25
|
+
*
|
|
26
|
+
* The executor runs it with the immutables of step 6, after the `before` chain, so a wire body, an
|
|
27
|
+
* in-process caller and a plugin are refused alike. It is the executor's own check, whatever
|
|
28
|
+
* `@owlmeans/planning` build `changesHelper.assertMutable` comes from.
|
|
29
|
+
*
|
|
30
|
+
* @throws {PlanningError} `planning:immutable:createdBy`
|
|
31
|
+
*/
|
|
32
|
+
assertCreatorFixed: (exec: TransitionExecution) => void
|
|
33
|
+
}
|
package/src/executor/creator.ts
CHANGED
|
@@ -1,51 +1,43 @@
|
|
|
1
1
|
import { PlanningError, TransitionAction, type PlanningScope, type TransitionExecution } from '@owlmeans/planning'
|
|
2
2
|
import { CREATOR } from './consts.local.js'
|
|
3
|
+
import type { CreatorHelper } from './creator/types.js'
|
|
3
4
|
|
|
4
5
|
const named = (value?: string | null): value is string => value != null && value !== ''
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
* The executor applies it to every execution, so an in-process facade create and a wire create
|
|
21
|
-
* stamp the same value.
|
|
22
|
-
*/
|
|
23
|
-
export const withCreator = (exec: TransitionExecution, scope: PlanningScope): TransitionExecution => {
|
|
24
|
-
if (exec.action !== TransitionAction.Create || exec.card == null || typeof exec.card !== 'object') {
|
|
25
|
-
return exec
|
|
7
|
+
export const createCreatorHelper = (): CreatorHelper => {
|
|
8
|
+
const creatorOf = (scope: PlanningScope): string | undefined =>
|
|
9
|
+
[scope.profileId, scope.userId, scope.actor?.profileId, scope.actor?.userId].find(named)
|
|
10
|
+
|
|
11
|
+
const withCreator = (exec: TransitionExecution, scope: PlanningScope): TransitionExecution => {
|
|
12
|
+
if (exec.action !== TransitionAction.Create || exec.card == null || typeof exec.card !== 'object') {
|
|
13
|
+
return exec
|
|
14
|
+
}
|
|
15
|
+
if (named(exec.card.createdBy)) {
|
|
16
|
+
return exec
|
|
17
|
+
}
|
|
18
|
+
const creator = creatorOf(scope)
|
|
19
|
+
|
|
20
|
+
return creator == null ? exec : { ...exec, card: { ...exec.card, createdBy: creator } }
|
|
26
21
|
}
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
|
|
23
|
+
const assertCreatorFixed = (exec: TransitionExecution): void => {
|
|
24
|
+
if ((exec.changes as Record<string, unknown> | undefined)?.[CREATOR] !== undefined
|
|
25
|
+
|| (exec.unset ?? []).some(path => path.split('.')[0] === CREATOR)) {
|
|
26
|
+
throw new PlanningError(`immutable:${CREATOR}`)
|
|
27
|
+
}
|
|
29
28
|
}
|
|
30
|
-
const creator = creatorOf(scope)
|
|
31
29
|
|
|
32
|
-
return
|
|
30
|
+
return { creatorOf, withCreator, assertCreatorFixed }
|
|
33
31
|
}
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
export const assertCreatorFixed = (exec: TransitionExecution): void => {
|
|
47
|
-
if ((exec.changes as Record<string, unknown> | undefined)?.[CREATOR] !== undefined
|
|
48
|
-
|| (exec.unset ?? []).some(path => path.split('.')[0] === CREATOR)) {
|
|
49
|
-
throw new PlanningError(`immutable:${CREATOR}`)
|
|
50
|
-
}
|
|
51
|
-
}
|
|
33
|
+
export const creatorHelper = createCreatorHelper()
|
|
34
|
+
|
|
35
|
+
/** @deprecated compat:factory-refactor — use `creatorHelper.creatorOf(…)` */
|
|
36
|
+
export const creatorOf = (scope: PlanningScope): string | undefined => creatorHelper.creatorOf(scope)
|
|
37
|
+
|
|
38
|
+
/** @deprecated compat:factory-refactor — use `creatorHelper.withCreator(…)` */
|
|
39
|
+
export const withCreator = (exec: TransitionExecution, scope: PlanningScope): TransitionExecution =>
|
|
40
|
+
creatorHelper.withCreator(exec, scope)
|
|
41
|
+
|
|
42
|
+
/** @deprecated compat:factory-refactor — use `creatorHelper.assertCreatorFixed(…)` */
|
|
43
|
+
export const assertCreatorFixed = (exec: TransitionExecution): void => creatorHelper.assertCreatorFixed(exec)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { PlanningFacade, PlanningSchemaRegistry, TransitionExecution, Workcard, WorkcardKind } from '@owlmeans/planning'
|
|
2
|
+
import type { Resolved } from '../types.js'
|
|
3
|
+
|
|
4
|
+
/** Steps 1 and 4 of the write path, over one service runtime. */
|
|
5
|
+
export interface ResolveUtils {
|
|
6
|
+
/**
|
|
7
|
+
* Step 1: a deep copy with trimmed text, `parents ∋ parent` on a draft, and no duplicate `unset`
|
|
8
|
+
* paths. The caller's object is never touched.
|
|
9
|
+
*/
|
|
10
|
+
normalizeExecution: (input: TransitionExecution) => TransitionExecution
|
|
11
|
+
/**
|
|
12
|
+
* The parent type's rule for a child of `kind`/`type`: a project lists the `cardTypes` (and
|
|
13
|
+
* `projectTypes`) it admits, and one declaring `scopedCardTypes` also admits a card type its layer
|
|
14
|
+
* (`view`, the parent project's resolved schemas) defines as data. Project types are code's alone.
|
|
15
|
+
*
|
|
16
|
+
* @throws {CardTypeNotAllowed}
|
|
17
|
+
*/
|
|
18
|
+
assertChildAllowed: (parent: Workcard, kind: WorkcardKind, type: string, view?: PlanningSchemaRegistry) => void
|
|
19
|
+
/** The layer a parent project admits data-defined children through — `undefined` without a schema port. */
|
|
20
|
+
childViewOf: (entityId: string, parent: Workcard) => Promise<PlanningSchemaRegistry | undefined>
|
|
21
|
+
/**
|
|
22
|
+
* Step 4: the card, its type and flow, its parent and — for a specification — its slot.
|
|
23
|
+
*
|
|
24
|
+
* @throws {WorkcardNotFound | PlanningScopeMismatch | UnknownWorkcardType | UnknownStatusFlow}
|
|
25
|
+
* @throws {ParentNotFound | CardTypeNotAllowed | PlanningError}
|
|
26
|
+
*/
|
|
27
|
+
resolveExecution: (facade: PlanningFacade, exec: TransitionExecution) => Promise<Resolved>
|
|
28
|
+
/** The project a transition is filed under: a project's own id, else its parent's project. */
|
|
29
|
+
projectFor: (resolved: Resolved, exec: TransitionExecution, cardId: string) => string | undefined
|
|
30
|
+
}
|