@owlmeans/server-planning 0.1.18-rc.21 → 0.1.18-rc.23
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/cards.d.ts +5 -3
- package/build/actions/cards.d.ts.map +1 -1
- package/build/actions/cards.js +1 -1
- package/build/actions/cards.js.map +1 -1
- 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/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/schemas.d.ts +5 -3
- package/build/actions/schemas.d.ts.map +1 -1
- package/build/actions/schemas.js.map +1 -1
- package/build/actions/specs.d.ts +4 -2
- package/build/actions/specs.d.ts.map +1 -1
- package/build/actions/specs.js.map +1 -1
- 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/src/actions/cards.ts +0 -48
- package/src/actions/schemas.ts +0 -87
package/src/conformance/cases.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
|
|
2
|
+
import { applyHelper, CardTypeNotAllowed, CommitState, IntrinsicStatus, PlanningError, PlanningSchemaKind, RelationshipRefused, SchemaConflict, SchemaInUse, SchemaInvalid, SchemaOrigin, SchemaSealed, SpecificationFormat, TransitionAction, UnknownWorkcardType, WorkcardConflict, WorkcardKind, WorkcardNotFound, type CommitEvent, type PlanningFacade, type PlanningStore, type StatusFlowSchema, type Transition, type TransitionExecution, type Unsubscribe, type Workcard, type WorkcardQuery, type WorkcardTypeSchema } from '@owlmeans/planning'
|
|
3
|
+
import { creatorHelper } from '../executor/creator.js'
|
|
4
|
+
import { assertHelper } from './assert.js'
|
|
5
|
+
import { conformanceFixturesOf } from './fixtures.js'
|
|
6
6
|
import { BOOK_TYPE, LIBRARY, PERIODICAL_TYPE } from './consts.js'
|
|
7
7
|
import type { ConformanceCase } from './types.js'
|
|
8
|
+
import { idHelper } from '@owlmeans/basic-ids'
|
|
9
|
+
|
|
10
|
+
const { check, rejects, same, sameSet } = assertHelper
|
|
8
11
|
|
|
9
12
|
/** A fresh organization per case, so cases never see each other's cards on a shared database. */
|
|
10
|
-
const organization = (): string => `conformance-${uuid()}`
|
|
13
|
+
const organization = (): string => `conformance-${idHelper.uuid()}`
|
|
11
14
|
|
|
12
15
|
const ids = async (planning: PlanningFacade, query: WorkcardQuery): Promise<string[]> =>
|
|
13
16
|
(await planning.cards.list({ ...query, size: 0 })).items.map(card => card.id!)
|
|
@@ -21,13 +24,14 @@ const cases: ConformanceCase[] = [
|
|
|
21
24
|
name: 'folds transitions in seq order and settles each one once',
|
|
22
25
|
run: async ({ facade }) => {
|
|
23
26
|
const planning = facade(organization())
|
|
27
|
+
const fixtures = conformanceFixturesOf(planning)
|
|
24
28
|
const events: CommitEvent[] = []
|
|
25
29
|
const unsubscribe: Unsubscribe = await planning.commits.subscribe(event => { events.push(event) })
|
|
26
30
|
try {
|
|
27
|
-
const branch = await createBranch(
|
|
28
|
-
const book = await createBook(
|
|
31
|
+
const branch = await fixtures.createBranch()
|
|
32
|
+
const book = await fixtures.createBook(branch.id!, 'Winter poems')
|
|
29
33
|
await execute(planning, { card: book.id!, action: TransitionAction.Update, changes: { title: 'Winter verses' } })
|
|
30
|
-
await transit(
|
|
34
|
+
await fixtures.transit(book.id!, 'lend')
|
|
31
35
|
|
|
32
36
|
const card = await planning.cards.get(book.id!)
|
|
33
37
|
same([card.seq, card.head, card.title, card.status, card.intrinsic], [3, 3, 'Winter verses', 'lent', IntrinsicStatus.InProgress], 'the folded card')
|
|
@@ -43,13 +47,14 @@ const cases: ConformanceCase[] = [
|
|
|
43
47
|
name: 'allocates seq against the head and refuses a stale expectSeq',
|
|
44
48
|
run: async ({ facade, store }) => {
|
|
45
49
|
const planning = facade(organization())
|
|
46
|
-
const
|
|
50
|
+
const fixtures = conformanceFixturesOf(planning)
|
|
51
|
+
const book = await fixtures.createBook((await fixtures.createBranch()).id!)
|
|
47
52
|
|
|
48
53
|
await execute(planning, { card: book.id!, action: TransitionAction.Update, changes: { title: 'First' }, expectSeq: 1 })
|
|
49
54
|
await rejects(planning.execute({ card: book.id!, action: TransitionAction.Update, changes: { title: 'Stale' }, expectSeq: 1 }),
|
|
50
55
|
WorkcardConflict, 'a stale expectSeq')
|
|
51
56
|
same(await store.transitions!.head(book.id!), 2, 'the head after two transitions')
|
|
52
|
-
same(await store.transitions!.nextSeq(`conformance-${uuid()}`, null), 1, 'a card with no log allocates 1')
|
|
57
|
+
same(await store.transitions!.nextSeq(`conformance-${idHelper.uuid()}`, null), 1, 'a card with no log allocates 1')
|
|
53
58
|
same((await planning.cards.get(book.id!)).title, 'First', 'the refused write left the card alone')
|
|
54
59
|
},
|
|
55
60
|
},
|
|
@@ -58,7 +63,8 @@ const cases: ConformanceCase[] = [
|
|
|
58
63
|
name: 'an idempotency key answers its first receipt',
|
|
59
64
|
run: async ({ facade }) => {
|
|
60
65
|
const planning = facade(organization())
|
|
61
|
-
const
|
|
66
|
+
const fixtures = conformanceFixturesOf(planning)
|
|
67
|
+
const book = await fixtures.createBook((await fixtures.createBranch()).id!)
|
|
62
68
|
|
|
63
69
|
const first = await planning.execute({ card: book.id!, action: TransitionAction.Update, changes: { title: 'Kept' }, key: 'import:1' }, { wait: true })
|
|
64
70
|
const again = await planning.execute({ card: book.id!, action: TransitionAction.Update, changes: { title: 'Lost' }, key: 'import:1' }, { wait: true })
|
|
@@ -73,14 +79,15 @@ const cases: ConformanceCase[] = [
|
|
|
73
79
|
name: 'keeps a create\'s createdBy — the scope\'s subject, or the one the caller named — through later writes',
|
|
74
80
|
run: async ({ facade }) => {
|
|
75
81
|
const planning = facade(organization())
|
|
76
|
-
const
|
|
82
|
+
const fixtures = conformanceFixturesOf(planning)
|
|
83
|
+
const subject = creatorHelper.creatorOf(planning.scope)
|
|
77
84
|
check(subject != null, 'the conformance facade acts as a signed-in person')
|
|
78
85
|
|
|
79
|
-
const branch = await createBranch(
|
|
80
|
-
const stamped = await createBook(
|
|
81
|
-
const named = await createBook(
|
|
86
|
+
const branch = await fixtures.createBranch()
|
|
87
|
+
const stamped = await fixtures.createBook(branch.id!, 'Stamped')
|
|
88
|
+
const named = await fixtures.createBook(branch.id!, 'Named', { createdBy: 'conformance-donor' })
|
|
82
89
|
await execute(planning, { card: stamped.id!, action: TransitionAction.Update, changes: { title: 'Stamped, revised' } })
|
|
83
|
-
await transit(
|
|
90
|
+
await fixtures.transit(stamped.id!, 'lend')
|
|
84
91
|
|
|
85
92
|
same([branch.createdBy, (await planning.cards.get(stamped.id!)).createdBy, (await planning.cards.get(named.id!)).createdBy],
|
|
86
93
|
[subject, subject, 'conformance-donor'], 'createdBy read back after later writes')
|
|
@@ -94,8 +101,9 @@ const cases: ConformanceCase[] = [
|
|
|
94
101
|
run: async ({ facade, store }) => {
|
|
95
102
|
const org = organization()
|
|
96
103
|
const planning = facade(org)
|
|
97
|
-
const
|
|
98
|
-
const
|
|
104
|
+
const fixtures = conformanceFixturesOf(planning)
|
|
105
|
+
const branch = await fixtures.createBranch()
|
|
106
|
+
const book = await fixtures.createBook(branch.id!, 'Owned')
|
|
99
107
|
const moves: TransitionExecution[] = [
|
|
100
108
|
{ card: book.id!, action: TransitionAction.Update, changes: { title: 'Taken', createdBy: 'conformance-intruder' } },
|
|
101
109
|
{ card: book.id!, action: TransitionAction.Update, unset: ['createdBy'] },
|
|
@@ -107,7 +115,7 @@ const cases: ConformanceCase[] = [
|
|
|
107
115
|
}
|
|
108
116
|
const kept = await planning.cards.get(book.id!)
|
|
109
117
|
same([kept.createdBy, kept.title, kept.seq, (await planning.transitions.list({ card: book.id! })).total],
|
|
110
|
-
[creatorOf(planning.scope), 'Owned', 1, 1], 'the refused writes left the card and its log alone')
|
|
118
|
+
[creatorHelper.creatorOf(planning.scope), 'Owned', 1, 1], 'the refused writes left the card and its log alone')
|
|
111
119
|
|
|
112
120
|
// Rows appended before the refusal existed: the store folds them exactly as the pure fold does.
|
|
113
121
|
const legacy: Transition[] = []
|
|
@@ -124,7 +132,7 @@ const cases: ConformanceCase[] = [
|
|
|
124
132
|
for (const row of legacy) {
|
|
125
133
|
same((await planning.commits.status(row.id!)).state, CommitState.Committed, `legacy row ${row.seq} settles committed`)
|
|
126
134
|
}
|
|
127
|
-
const expected = legacy.reduce<Workcard | null>((card, row) => applyTransition(card, row), kept)!
|
|
135
|
+
const expected = legacy.reduce<Workcard | null>((card, row) => applyHelper.applyTransition(card, row), kept)!
|
|
128
136
|
const folded = await planning.cards.get(book.id!)
|
|
129
137
|
same([folded.title, folded.seq, folded.createdBy], [expected.title, expected.seq, expected.createdBy], 'the legacy rows folded')
|
|
130
138
|
await execute(planning, { card: book.id!, action: TransitionAction.Update, changes: { title: 'After' } })
|
|
@@ -136,18 +144,19 @@ const cases: ConformanceCase[] = [
|
|
|
136
144
|
name: 'transit moves the intrinsic state and closedAt, and a second flow leaves the primary alone',
|
|
137
145
|
run: async ({ facade }) => {
|
|
138
146
|
const planning = facade(organization())
|
|
139
|
-
const
|
|
147
|
+
const fixtures = conformanceFixturesOf(planning)
|
|
148
|
+
const book = await fixtures.createBook((await fixtures.createBranch()).id!)
|
|
140
149
|
|
|
141
|
-
await transit(
|
|
150
|
+
await fixtures.transit(book.id!, 'retire')
|
|
142
151
|
const retired = await planning.cards.get(book.id!)
|
|
143
152
|
same([retired.status, retired.intrinsic], ['retired', IntrinsicStatus.Closed], 'retired')
|
|
144
153
|
check(typeof retired.closedAt === 'string', 'closedAt is set on entering closed')
|
|
145
154
|
|
|
146
|
-
await transit(
|
|
155
|
+
await fixtures.transit(book.id!, 'restore')
|
|
147
156
|
const restored = await planning.cards.get(book.id!)
|
|
148
157
|
same([restored.status, restored.intrinsic, restored.closedAt], ['shelved', IntrinsicStatus.Planned, undefined], 'closedAt cleared on leaving closed')
|
|
149
158
|
|
|
150
|
-
await transit(
|
|
159
|
+
await fixtures.transit(book.id!, 'review', { flow: LIBRARY.review })
|
|
151
160
|
const reviewed = await planning.cards.get(book.id!)
|
|
152
161
|
same([reviewed.status, reviewed.flows[LIBRARY.review], reviewed.intrinsic], ['shelved', 'reviewed', IntrinsicStatus.Planned], 'the second flow moved alone')
|
|
153
162
|
},
|
|
@@ -158,12 +167,13 @@ const cases: ConformanceCase[] = [
|
|
|
158
167
|
run: async ({ facade }) => {
|
|
159
168
|
const org = organization()
|
|
160
169
|
const planning = facade(org)
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
const
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
await
|
|
170
|
+
const fixtures = conformanceFixturesOf(planning)
|
|
171
|
+
const east = await fixtures.createBranch('East branch')
|
|
172
|
+
const west = await fixtures.createBranch('West branch')
|
|
173
|
+
const poems = await fixtures.createBook(east.id!, 'Winter poems', { labels: ['rare'], fields: { genre: 'poetry', signed: true } })
|
|
174
|
+
const atlas = await fixtures.createBook(east.id!, 'River atlas', { labels: ['new'], parents: [west.id!], fields: { genre: 'history' } })
|
|
175
|
+
const harbour = await fixtures.createBook(east.id!, 'Quiet harbour', { fields: { genre: 'fiction', signed: false } })
|
|
176
|
+
await fixtures.transit(atlas.id!, 'lend')
|
|
167
177
|
const since = (await planning.cards.get(atlas.id!)).updatedAt!
|
|
168
178
|
|
|
169
179
|
sameSet(await ids(planning, { within: west.id }), [atlas.id], 'within')
|
|
@@ -189,8 +199,9 @@ const cases: ConformanceCase[] = [
|
|
|
189
199
|
name: 'lists specifications only when the query asks for them',
|
|
190
200
|
run: async ({ facade }) => {
|
|
191
201
|
const planning = facade(organization())
|
|
192
|
-
const
|
|
193
|
-
const
|
|
202
|
+
const fixtures = conformanceFixturesOf(planning)
|
|
203
|
+
const branch = await fixtures.createBranch()
|
|
204
|
+
const book = await fixtures.createBook(branch.id!)
|
|
194
205
|
const model = await planning.model(book)
|
|
195
206
|
const summary = (await model.write('summary', '{"lines":["a"]}', { wait: true })).card!
|
|
196
207
|
const charter = (await (await planning.model(branch)).write('charter', '# Charter', { wait: true })).card!
|
|
@@ -208,9 +219,10 @@ const cases: ConformanceCase[] = [
|
|
|
208
219
|
name: 'pages and sorts a list, counting the whole match',
|
|
209
220
|
run: async ({ facade }) => {
|
|
210
221
|
const planning = facade(organization())
|
|
211
|
-
const
|
|
222
|
+
const fixtures = conformanceFixturesOf(planning)
|
|
223
|
+
const branch = await fixtures.createBranch()
|
|
212
224
|
for (const order of [5, 3, 1, 4, 2]) {
|
|
213
|
-
await createBook(
|
|
225
|
+
await fixtures.createBook(branch.id!, `Volume ${order}`, { order })
|
|
214
226
|
}
|
|
215
227
|
|
|
216
228
|
const page = await planning.cards.list({ parent: branch.id, sort: ['order'], size: 2, page: 1 })
|
|
@@ -225,15 +237,16 @@ const cases: ConformanceCase[] = [
|
|
|
225
237
|
name: 'summarizes direct children by intrinsic state',
|
|
226
238
|
run: async ({ facade }) => {
|
|
227
239
|
const planning = facade(organization())
|
|
228
|
-
const
|
|
229
|
-
const
|
|
230
|
-
const
|
|
231
|
-
const
|
|
232
|
-
await createBook(
|
|
233
|
-
await
|
|
234
|
-
await transit(
|
|
235
|
-
|
|
236
|
-
await
|
|
240
|
+
const fixtures = conformanceFixturesOf(planning)
|
|
241
|
+
const branch = await fixtures.createBranch()
|
|
242
|
+
const empty = await fixtures.createBranch('Empty branch')
|
|
243
|
+
const lent = await fixtures.createBook(branch.id!, 'Lent')
|
|
244
|
+
const retired = await fixtures.createBook(branch.id!, 'Retired')
|
|
245
|
+
await fixtures.createBook(branch.id!, 'Shelved')
|
|
246
|
+
await fixtures.transit(lent.id!, 'lend')
|
|
247
|
+
await fixtures.transit(retired.id!, 'retire')
|
|
248
|
+
const annex = await fixtures.createBranch('Annex', { parent: branch.id })
|
|
249
|
+
await fixtures.createBook(annex.id!, 'Deeper')
|
|
237
250
|
await (await planning.model(branch)).write('charter', '# Charter', { wait: true })
|
|
238
251
|
|
|
239
252
|
const summary = await planning.cards.summary([branch.id!, empty.id!])
|
|
@@ -248,8 +261,9 @@ const cases: ConformanceCase[] = [
|
|
|
248
261
|
name: 'answers a specification current, listed, every document and its revisions',
|
|
249
262
|
run: async ({ facade }) => {
|
|
250
263
|
const planning = facade(organization())
|
|
251
|
-
const
|
|
252
|
-
const
|
|
264
|
+
const fixtures = conformanceFixturesOf(planning)
|
|
265
|
+
const branch = await fixtures.createBranch()
|
|
266
|
+
const book = await fixtures.createBook(branch.id!)
|
|
253
267
|
const model = await planning.model(book)
|
|
254
268
|
await model.write('summary', '{"lines":["first"]}', { wait: true })
|
|
255
269
|
await (await planning.model(book.id!)).write('summary', '{"lines":["second"]}', { wait: true })
|
|
@@ -275,10 +289,11 @@ const cases: ConformanceCase[] = [
|
|
|
275
289
|
name: 'keeps links: both ends, the single constraint, unlink and a delete dropping edges',
|
|
276
290
|
run: async ({ facade }) => {
|
|
277
291
|
const planning = facade(organization())
|
|
278
|
-
const
|
|
279
|
-
const
|
|
280
|
-
const
|
|
281
|
-
const
|
|
292
|
+
const fixtures = conformanceFixturesOf(planning)
|
|
293
|
+
const branch = await fixtures.createBranch()
|
|
294
|
+
const first = await fixtures.createBook(branch.id!, 'First')
|
|
295
|
+
const second = await fixtures.createBook(branch.id!, 'Second')
|
|
296
|
+
const third = await fixtures.createBook(branch.id!, 'Third')
|
|
282
297
|
|
|
283
298
|
await execute(planning, { card: second.id!, action: TransitionAction.Link, link: { type: 'sequel-of', to: first.id! } })
|
|
284
299
|
same((await planning.relationships.list({ from: second.id })).total, 1, 'from')
|
|
@@ -301,9 +316,10 @@ const cases: ConformanceCase[] = [
|
|
|
301
316
|
name: 'isolates organizations on every read and write',
|
|
302
317
|
run: async ({ facade }) => {
|
|
303
318
|
const mine = facade(organization())
|
|
319
|
+
const fixtures = conformanceFixturesOf(mine)
|
|
304
320
|
const theirs = facade(organization())
|
|
305
|
-
const branch = await createBranch(
|
|
306
|
-
const book = await createBook(
|
|
321
|
+
const branch = await fixtures.createBranch()
|
|
322
|
+
const book = await fixtures.createBook(branch.id!)
|
|
307
323
|
const receipt = await mine.execute({ card: book.id!, action: TransitionAction.Update, changes: { title: 'Mine' } }, { wait: true })
|
|
308
324
|
|
|
309
325
|
same(await theirs.cards.load(book.id!), null, 'load')
|
|
@@ -322,15 +338,16 @@ const cases: ConformanceCase[] = [
|
|
|
322
338
|
name: 'purges everything under a deleted project, keeping at most its own delete as a tombstone',
|
|
323
339
|
run: async ({ facade }) => {
|
|
324
340
|
const planning = facade(organization())
|
|
325
|
-
const
|
|
326
|
-
const
|
|
327
|
-
const
|
|
328
|
-
const
|
|
341
|
+
const fixtures = conformanceFixturesOf(planning)
|
|
342
|
+
const doomed = await fixtures.createBranch('Doomed branch')
|
|
343
|
+
const kept = await fixtures.createBranch('Kept branch')
|
|
344
|
+
const first = await fixtures.createBook(doomed.id!, 'First')
|
|
345
|
+
const second = await fixtures.createBook(doomed.id!, 'Second')
|
|
329
346
|
await execute(planning, { card: second.id!, action: TransitionAction.Link, link: { type: 'sequel-of', to: first.id! } })
|
|
330
347
|
const summary = (await (await planning.model(first)).write('summary', '{}', { wait: true })).card!
|
|
331
|
-
const annex = await createBranch(
|
|
332
|
-
const deeper = await createBook(
|
|
333
|
-
const survivor = await createBook(
|
|
348
|
+
const annex = await fixtures.createBranch('Annex', { parent: doomed.id })
|
|
349
|
+
const deeper = await fixtures.createBook(annex.id!, 'Deeper')
|
|
350
|
+
const survivor = await fixtures.createBook(kept.id!, 'Survivor')
|
|
334
351
|
|
|
335
352
|
const deleted = await planning.execute({ card: doomed.id!, action: TransitionAction.Delete }, { wait: true })
|
|
336
353
|
|
|
@@ -352,7 +369,8 @@ const cases: ConformanceCase[] = [
|
|
|
352
369
|
run: async ({ facade, store }) => {
|
|
353
370
|
const org = organization()
|
|
354
371
|
const planning = facade(org)
|
|
355
|
-
const
|
|
372
|
+
const fixtures = conformanceFixturesOf(planning)
|
|
373
|
+
const book = await fixtures.createBook((await fixtures.createBranch()).id!)
|
|
356
374
|
// A row two past the card with nothing between — its allocation lost long ago.
|
|
357
375
|
const rogue = await store.transitions!.append({
|
|
358
376
|
entityId: org, card: book.id!, kind: WorkcardKind.Card, type: LIBRARY.book, seq: 3,
|
|
@@ -375,6 +393,7 @@ const cases: ConformanceCase[] = [
|
|
|
375
393
|
needs: ['schemas'],
|
|
376
394
|
run: async ({ facade }) => {
|
|
377
395
|
const planning = facade(organization())
|
|
396
|
+
const fixtures = conformanceFixturesOf(planning)
|
|
378
397
|
const pamphlets: StatusFlowSchema = {
|
|
379
398
|
id: 'library:pamphlet-life', version: 1,
|
|
380
399
|
statuses: [{ key: 'stacked', intrinsic: IntrinsicStatus.Planned, initial: true }, { key: 'taken', intrinsic: IntrinsicStatus.Closed }],
|
|
@@ -387,11 +406,11 @@ const cases: ConformanceCase[] = [
|
|
|
387
406
|
same(written.map(record => [record.kind, record.key, record.version]),
|
|
388
407
|
[[PlanningSchemaKind.Flow, pamphlets.id, 1], [PlanningSchemaKind.Type, pamphlet.type, 1]], 'flows first, then types')
|
|
389
408
|
|
|
390
|
-
const branch = await createBranch(
|
|
409
|
+
const branch = await fixtures.createBranch()
|
|
391
410
|
const card = await execute(planning, { card: { kind: WorkcardKind.Card, type: pamphlet.type, parent: branch.id!, title: 'Opening hours' }, action: TransitionAction.Create })
|
|
392
411
|
same([card?.status, card?.intrinsic], ['stacked', IntrinsicStatus.Planned], 'the data-defined flow started it')
|
|
393
412
|
same((await planning.model(card!)).available().map(rule => rule.name), ['take'], 'the model reads the layer')
|
|
394
|
-
await transit(
|
|
413
|
+
await fixtures.transit(card!.id!, 'take')
|
|
395
414
|
|
|
396
415
|
const room = await execute(planning, { card: { kind: WorkcardKind.Project, type: LIBRARY.room, title: 'Reading room' }, action: TransitionAction.Create })
|
|
397
416
|
await rejects(planning.execute({ card: { kind: WorkcardKind.Card, type: pamphlet.type, parent: room!.id!, title: 'Refused' }, action: TransitionAction.Create }),
|
|
@@ -408,8 +427,9 @@ const cases: ConformanceCase[] = [
|
|
|
408
427
|
needs: ['schemas'],
|
|
409
428
|
run: async ({ facade }) => {
|
|
410
429
|
const planning = facade(organization())
|
|
411
|
-
const
|
|
412
|
-
const
|
|
430
|
+
const fixtures = conformanceFixturesOf(planning)
|
|
431
|
+
const branch = await fixtures.createBranch()
|
|
432
|
+
const other = await fixtures.createBranch('Other branch')
|
|
413
433
|
await planning.definitions!.define({ types: [{ ...PERIODICAL_TYPE, label: 'Magazine' }] })
|
|
414
434
|
await planning.definitions!.define({ types: [{ ...PERIODICAL_TYPE, label: 'Zine' }] }, { project: branch.id })
|
|
415
435
|
|
|
@@ -450,6 +470,7 @@ const cases: ConformanceCase[] = [
|
|
|
450
470
|
needs: ['schemas'],
|
|
451
471
|
run: async ({ facade }) => {
|
|
452
472
|
const planning = facade(organization())
|
|
473
|
+
const fixtures = conformanceFixturesOf(planning)
|
|
453
474
|
const definitions = planning.definitions!
|
|
454
475
|
const repair: StatusFlowSchema = {
|
|
455
476
|
id: 'library:repair', version: 1,
|
|
@@ -464,15 +485,15 @@ const cases: ConformanceCase[] = [
|
|
|
464
485
|
type: 'library:folio', kind: WorkcardKind.Card, version: 1, fields: { type: 'object' }, flows: [repair.id], specifications: [],
|
|
465
486
|
}
|
|
466
487
|
await definitions.define({ flows: [repair], types: [folio] })
|
|
467
|
-
const branch = await createBranch(
|
|
488
|
+
const branch = await fixtures.createBranch()
|
|
468
489
|
const card = await execute(planning, { card: { kind: WorkcardKind.Card, type: folio.type, parent: branch.id!, title: 'Old map' }, action: TransitionAction.Create })
|
|
469
|
-
await transit(
|
|
490
|
+
await fixtures.transit(card!.id!, 'mend')
|
|
470
491
|
|
|
471
492
|
await rejects(definitions.retire(PlanningSchemaKind.Flow, repair.id), SchemaInUse, 'a flow a live type runs')
|
|
472
493
|
|
|
473
494
|
// The flow changes under the card: `mending` is no longer declared, and the card still moves.
|
|
474
495
|
await definitions.define({ flows: [{ ...repair, statuses: [repair.statuses[0], repair.statuses[2]], transitions: [{ name: 'finish', from: ['queued'], to: 'done' }] }] })
|
|
475
|
-
await transit(
|
|
496
|
+
await fixtures.transit(card!.id!, 'finish')
|
|
476
497
|
same((await planning.cards.get(card!.id!)).status, 'done', 'an undeclared status follows the rule of the name')
|
|
477
498
|
|
|
478
499
|
await definitions.retire(PlanningSchemaKind.Type, folio.type)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A clock that never repeats an instant — what a conformance service is booted with, so an
|
|
3
|
+
* `updatedSince` or an ordering by `at` is deterministic on a fast store.
|
|
4
|
+
*/
|
|
5
|
+
export const conformanceClock = (): (() => string) => {
|
|
6
|
+
let last = 0
|
|
7
|
+
return () => {
|
|
8
|
+
last = Math.max(Date.now(), last + 1)
|
|
9
|
+
return new Date(last).toISOString()
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type WorkcardTypeSchema, CodeScope, CodeStyle, SpecificationFormat, WorkcardKind } from '@owlmeans/planning'
|
|
1
|
+
import { type AnyTypeSchema, type PlanningPlugin, type StatusFlowSchema, type WorkcardTypeSchema, CodeScope, CodeStyle, IntrinsicStatus, SpecificationFormat, WorkcardKind } from '@owlmeans/planning'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* The conformance vocabulary: a lending library. Branches (projects) hold books and periodicals
|
|
@@ -42,3 +42,89 @@ export const PERIODICAL_TYPE: WorkcardTypeSchema = {
|
|
|
42
42
|
flows: [LIBRARY.circulation],
|
|
43
43
|
specifications: [],
|
|
44
44
|
}
|
|
45
|
+
|
|
46
|
+
const flows: StatusFlowSchema[] = [
|
|
47
|
+
{
|
|
48
|
+
id: LIBRARY.circulation,
|
|
49
|
+
version: 1,
|
|
50
|
+
statuses: [
|
|
51
|
+
{ key: 'shelved', intrinsic: IntrinsicStatus.Planned, initial: true },
|
|
52
|
+
{ key: 'lent', intrinsic: IntrinsicStatus.InProgress },
|
|
53
|
+
{ key: 'retired', intrinsic: IntrinsicStatus.Closed, terminal: true },
|
|
54
|
+
],
|
|
55
|
+
transitions: [
|
|
56
|
+
{ name: 'lend', from: ['shelved'], to: 'lent', explicit: true },
|
|
57
|
+
{ name: 'return', from: ['lent'], to: 'shelved', explicit: true },
|
|
58
|
+
{ name: 'retire', from: '*', to: 'retired' },
|
|
59
|
+
{ name: 'restore', from: ['retired'], to: 'shelved' },
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: LIBRARY.review,
|
|
64
|
+
version: 1,
|
|
65
|
+
statuses: [
|
|
66
|
+
{ key: 'unreviewed', intrinsic: IntrinsicStatus.Planned, initial: true },
|
|
67
|
+
{ key: 'reviewed', intrinsic: IntrinsicStatus.Closed },
|
|
68
|
+
],
|
|
69
|
+
transitions: [{ name: 'review', from: ['unreviewed'], to: 'reviewed' }],
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: LIBRARY.branchLife,
|
|
73
|
+
version: 1,
|
|
74
|
+
statuses: [
|
|
75
|
+
{ key: 'open', intrinsic: IntrinsicStatus.Planned, initial: true },
|
|
76
|
+
{ key: 'active', intrinsic: IntrinsicStatus.InProgress },
|
|
77
|
+
],
|
|
78
|
+
transitions: [{ name: 'activate', from: ['open'], to: 'active' }],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: LIBRARY.pageLife,
|
|
82
|
+
version: 1,
|
|
83
|
+
statuses: [{ key: 'draft', intrinsic: IntrinsicStatus.Planned, initial: true }],
|
|
84
|
+
transitions: [],
|
|
85
|
+
},
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
const types: AnyTypeSchema[] = [
|
|
89
|
+
{
|
|
90
|
+
type: LIBRARY.branch,
|
|
91
|
+
kind: WorkcardKind.Project,
|
|
92
|
+
version: 1,
|
|
93
|
+
fields: { type: 'object', additionalProperties: true },
|
|
94
|
+
flows: [LIBRARY.branchLife],
|
|
95
|
+
cardTypes: [LIBRARY.book, LIBRARY.periodical],
|
|
96
|
+
projectTypes: [LIBRARY.branch],
|
|
97
|
+
scopedCardTypes: true,
|
|
98
|
+
specifications: [
|
|
99
|
+
{ category: 'charter', format: SpecificationFormat.Markdown },
|
|
100
|
+
{ category: 'notes', format: SpecificationFormat.Markdown, multiple: true },
|
|
101
|
+
],
|
|
102
|
+
code: { style: CodeStyle.Slug, uniqueWithin: CodeScope.Entity, mutable: true },
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
type: LIBRARY.room,
|
|
106
|
+
kind: WorkcardKind.Project,
|
|
107
|
+
version: 1,
|
|
108
|
+
fields: { type: 'object', additionalProperties: true },
|
|
109
|
+
flows: [LIBRARY.branchLife],
|
|
110
|
+
cardTypes: [LIBRARY.book],
|
|
111
|
+
specifications: [],
|
|
112
|
+
},
|
|
113
|
+
BOOK_TYPE,
|
|
114
|
+
PERIODICAL_TYPE,
|
|
115
|
+
{
|
|
116
|
+
type: LIBRARY.page,
|
|
117
|
+
kind: WorkcardKind.Specification,
|
|
118
|
+
version: 1,
|
|
119
|
+
fields: { type: 'object', additionalProperties: true },
|
|
120
|
+
flows: [LIBRARY.pageLife],
|
|
121
|
+
specifications: [],
|
|
122
|
+
},
|
|
123
|
+
]
|
|
124
|
+
|
|
125
|
+
/** The types and flows every conformance case runs against — register it on the service under test. */
|
|
126
|
+
export const planningConformancePlugin: PlanningPlugin = Object.freeze({
|
|
127
|
+
name: 'planning-conformance',
|
|
128
|
+
order: 0,
|
|
129
|
+
schemas: { types, flows },
|
|
130
|
+
})
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TransitionReceipt, Workcard } from '@owlmeans/planning'
|
|
2
|
+
|
|
3
|
+
/** The records a conformance case creates and moves, through one organization's facade. */
|
|
4
|
+
export interface ConformanceFixtures {
|
|
5
|
+
/** A branch (project) at the root, written and committed. */
|
|
6
|
+
createBranch: (title?: string, extra?: Record<string, unknown>) => Promise<Workcard>
|
|
7
|
+
/** A book (card) under a branch, written and committed. */
|
|
8
|
+
createBook: (branch: string, title?: string, extra?: Record<string, unknown>) => Promise<Workcard>
|
|
9
|
+
/** A transit of a card, waited until it commits. */
|
|
10
|
+
transit: (card: string, transition: string, extra?: { flow?: string }) => Promise<TransitionReceipt>
|
|
11
|
+
}
|
|
@@ -1,121 +1,46 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { memoHelper } from '@owlmeans/context'
|
|
2
|
+
import { TransitionAction, WorkcardKind, type PlanningFacade, type TransitionReceipt, type Workcard } from '@owlmeans/planning'
|
|
3
|
+
import { LIBRARY } from './consts.js'
|
|
4
|
+
import type { ConformanceFixtures } from './fixtures/types.js'
|
|
4
5
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
{ key: 'retired', intrinsic: IntrinsicStatus.Closed, terminal: true },
|
|
13
|
-
],
|
|
14
|
-
transitions: [
|
|
15
|
-
{ name: 'lend', from: ['shelved'], to: 'lent', explicit: true },
|
|
16
|
-
{ name: 'return', from: ['lent'], to: 'shelved', explicit: true },
|
|
17
|
-
{ name: 'retire', from: '*', to: 'retired' },
|
|
18
|
-
{ name: 'restore', from: ['retired'], to: 'shelved' },
|
|
19
|
-
],
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
id: LIBRARY.review,
|
|
23
|
-
version: 1,
|
|
24
|
-
statuses: [
|
|
25
|
-
{ key: 'unreviewed', intrinsic: IntrinsicStatus.Planned, initial: true },
|
|
26
|
-
{ key: 'reviewed', intrinsic: IntrinsicStatus.Closed },
|
|
27
|
-
],
|
|
28
|
-
transitions: [{ name: 'review', from: ['unreviewed'], to: 'reviewed' }],
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
id: LIBRARY.branchLife,
|
|
32
|
-
version: 1,
|
|
33
|
-
statuses: [
|
|
34
|
-
{ key: 'open', intrinsic: IntrinsicStatus.Planned, initial: true },
|
|
35
|
-
{ key: 'active', intrinsic: IntrinsicStatus.InProgress },
|
|
36
|
-
],
|
|
37
|
-
transitions: [{ name: 'activate', from: ['open'], to: 'active' }],
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
id: LIBRARY.pageLife,
|
|
41
|
-
version: 1,
|
|
42
|
-
statuses: [{ key: 'draft', intrinsic: IntrinsicStatus.Planned, initial: true }],
|
|
43
|
-
transitions: [],
|
|
44
|
-
},
|
|
45
|
-
]
|
|
6
|
+
export const makeConformanceFixtures = (planning: PlanningFacade): ConformanceFixtures => {
|
|
7
|
+
const createBranch = async (
|
|
8
|
+
title: string = 'Riverside branch', extra: Record<string, unknown> = {}
|
|
9
|
+
): Promise<Workcard> => (await planning.execute({
|
|
10
|
+
card: { kind: WorkcardKind.Project, type: LIBRARY.branch, title, ...extra },
|
|
11
|
+
action: TransitionAction.Create,
|
|
12
|
+
}, { wait: true })).card!
|
|
46
13
|
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
kind: WorkcardKind.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
flows: [LIBRARY.branchLife],
|
|
54
|
-
cardTypes: [LIBRARY.book, LIBRARY.periodical],
|
|
55
|
-
projectTypes: [LIBRARY.branch],
|
|
56
|
-
scopedCardTypes: true,
|
|
57
|
-
specifications: [
|
|
58
|
-
{ category: 'charter', format: SpecificationFormat.Markdown },
|
|
59
|
-
{ category: 'notes', format: SpecificationFormat.Markdown, multiple: true },
|
|
60
|
-
],
|
|
61
|
-
code: { style: CodeStyle.Slug, uniqueWithin: CodeScope.Entity, mutable: true },
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
type: LIBRARY.room,
|
|
65
|
-
kind: WorkcardKind.Project,
|
|
66
|
-
version: 1,
|
|
67
|
-
fields: { type: 'object', additionalProperties: true },
|
|
68
|
-
flows: [LIBRARY.branchLife],
|
|
69
|
-
cardTypes: [LIBRARY.book],
|
|
70
|
-
specifications: [],
|
|
71
|
-
},
|
|
72
|
-
BOOK_TYPE,
|
|
73
|
-
PERIODICAL_TYPE,
|
|
74
|
-
{
|
|
75
|
-
type: LIBRARY.page,
|
|
76
|
-
kind: WorkcardKind.Specification,
|
|
77
|
-
version: 1,
|
|
78
|
-
fields: { type: 'object', additionalProperties: true },
|
|
79
|
-
flows: [LIBRARY.pageLife],
|
|
80
|
-
specifications: [],
|
|
81
|
-
},
|
|
82
|
-
]
|
|
14
|
+
const createBook = async (
|
|
15
|
+
branch: string, title: string = 'A book', extra: Record<string, unknown> = {}
|
|
16
|
+
): Promise<Workcard> => (await planning.execute({
|
|
17
|
+
card: { kind: WorkcardKind.Card, type: LIBRARY.book, parent: branch, title, ...extra },
|
|
18
|
+
action: TransitionAction.Create,
|
|
19
|
+
}, { wait: true })).card!
|
|
83
20
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
})
|
|
21
|
+
const transit = async (
|
|
22
|
+
card: string, transition: string, extra: { flow?: string } = {}
|
|
23
|
+
): Promise<TransitionReceipt> => await planning.execute({
|
|
24
|
+
card, action: TransitionAction.Transit, transition, ...extra,
|
|
25
|
+
}, { wait: true })
|
|
90
26
|
|
|
91
|
-
|
|
92
|
-
* A clock that never repeats an instant — what a conformance service is booted with, so an
|
|
93
|
-
* `updatedSince` or an ordering by `at` is deterministic on a fast store.
|
|
94
|
-
*/
|
|
95
|
-
export const conformanceClock = (): (() => string) => {
|
|
96
|
-
let last = 0
|
|
97
|
-
return () => {
|
|
98
|
-
last = Math.max(Date.now(), last + 1)
|
|
99
|
-
return new Date(last).toISOString()
|
|
100
|
-
}
|
|
27
|
+
return { createBranch, createBook, transit }
|
|
101
28
|
}
|
|
102
29
|
|
|
30
|
+
/** The fixtures of one facade — one per facade. */
|
|
31
|
+
export const conformanceFixturesOf = memoHelper.oncePer(makeConformanceFixtures)
|
|
32
|
+
|
|
33
|
+
/** @deprecated compat:factory-refactor — use `conformanceFixturesOf(planning).createBranch(…)` */
|
|
103
34
|
export const createBranch = async (
|
|
104
35
|
planning: PlanningFacade, title: string = 'Riverside branch', extra: Record<string, unknown> = {}
|
|
105
|
-
): Promise<Workcard> =>
|
|
106
|
-
card: { kind: WorkcardKind.Project, type: LIBRARY.branch, title, ...extra },
|
|
107
|
-
action: TransitionAction.Create,
|
|
108
|
-
}, { wait: true })).card!
|
|
36
|
+
): Promise<Workcard> => await conformanceFixturesOf(planning).createBranch(title, extra)
|
|
109
37
|
|
|
38
|
+
/** @deprecated compat:factory-refactor — use `conformanceFixturesOf(planning).createBook(…)` */
|
|
110
39
|
export const createBook = async (
|
|
111
40
|
planning: PlanningFacade, branch: string, title: string = 'A book', extra: Record<string, unknown> = {}
|
|
112
|
-
): Promise<Workcard> =>
|
|
113
|
-
card: { kind: WorkcardKind.Card, type: LIBRARY.book, parent: branch, title, ...extra },
|
|
114
|
-
action: TransitionAction.Create,
|
|
115
|
-
}, { wait: true })).card!
|
|
41
|
+
): Promise<Workcard> => await conformanceFixturesOf(planning).createBook(branch, title, extra)
|
|
116
42
|
|
|
43
|
+
/** @deprecated compat:factory-refactor — use `conformanceFixturesOf(planning).transit(…)` */
|
|
117
44
|
export const transit = async (
|
|
118
45
|
planning: PlanningFacade, card: string, transition: string, extra: { flow?: string } = {}
|
|
119
|
-
): Promise<TransitionReceipt> => await planning.
|
|
120
|
-
card, action: TransitionAction.Transit, transition, ...extra,
|
|
121
|
-
}, { wait: true })
|
|
46
|
+
): Promise<TransitionReceipt> => await conformanceFixturesOf(planning).transit(card, transition, extra)
|
package/src/conformance/index.ts
CHANGED
|
@@ -7,5 +7,8 @@
|
|
|
7
7
|
export * from './assert.js'
|
|
8
8
|
export * from './cases.js'
|
|
9
9
|
export type * from './types.js'
|
|
10
|
+
export type * from './assert/types.js'
|
|
11
|
+
export type * from './fixtures/types.js'
|
|
12
|
+
export * from './clock.js'
|
|
10
13
|
export * from './fixtures.js'
|
|
11
14
|
export * from './consts.js'
|