@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.
Files changed (314) hide show
  1. package/README.md +24 -17
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/server-planning/SKILL.md +28 -28
  4. package/build/actions/apply-schema-request.d.ts +8 -0
  5. package/build/actions/apply-schema-request.d.ts.map +1 -0
  6. package/build/actions/apply-schema-request.js +31 -0
  7. package/build/actions/apply-schema-request.js.map +1 -0
  8. package/build/actions/commit.d.ts +3 -3
  9. package/build/actions/commit.d.ts.map +1 -1
  10. package/build/actions/commit.js +5 -4
  11. package/build/actions/commit.js.map +1 -1
  12. package/build/actions/define-schemas.d.ts +13 -0
  13. package/build/actions/define-schemas.d.ts.map +1 -0
  14. package/build/actions/define-schemas.js +28 -0
  15. package/build/actions/define-schemas.js.map +1 -0
  16. package/build/actions/execute.d.ts +8 -28
  17. package/build/actions/execute.d.ts.map +1 -1
  18. package/build/actions/execute.js +17 -101
  19. package/build/actions/execute.js.map +1 -1
  20. package/build/actions/execution/types.d.ts +34 -0
  21. package/build/actions/execution/types.d.ts.map +1 -0
  22. package/build/actions/execution/types.js +2 -0
  23. package/build/actions/execution/types.js.map +1 -0
  24. package/build/actions/execution.d.ts +4 -0
  25. package/build/actions/execution.d.ts.map +1 -0
  26. package/build/actions/execution.js +79 -0
  27. package/build/actions/execution.js.map +1 -0
  28. package/build/actions/get-card.d.ts +6 -0
  29. package/build/actions/get-card.d.ts.map +1 -0
  30. package/build/actions/get-card.js +9 -0
  31. package/build/actions/get-card.js.map +1 -0
  32. package/build/actions/get-specification.d.ts +6 -0
  33. package/build/actions/get-specification.d.ts.map +1 -0
  34. package/build/actions/get-specification.js +9 -0
  35. package/build/actions/get-specification.js.map +1 -0
  36. package/build/actions/index.d.ts +12 -3
  37. package/build/actions/index.d.ts.map +1 -1
  38. package/build/actions/index.js +11 -3
  39. package/build/actions/index.js.map +1 -1
  40. package/build/actions/links.d.ts +3 -3
  41. package/build/actions/links.d.ts.map +1 -1
  42. package/build/actions/links.js +6 -5
  43. package/build/actions/links.js.map +1 -1
  44. package/build/actions/list-card-specifications.d.ts +6 -0
  45. package/build/actions/list-card-specifications.d.ts.map +1 -0
  46. package/build/actions/list-card-specifications.js +11 -0
  47. package/build/actions/list-card-specifications.js.map +1 -0
  48. package/build/actions/list-card-transitions.d.ts +6 -0
  49. package/build/actions/list-card-transitions.d.ts.map +1 -0
  50. package/build/actions/list-card-transitions.js +11 -0
  51. package/build/actions/list-card-transitions.js.map +1 -0
  52. package/build/actions/list-cards.d.ts +6 -0
  53. package/build/actions/list-cards.d.ts.map +1 -0
  54. package/build/actions/list-cards.js +10 -0
  55. package/build/actions/list-cards.js.map +1 -0
  56. package/build/actions/list-schemas.d.ts +13 -0
  57. package/build/actions/list-schemas.d.ts.map +1 -0
  58. package/build/actions/list-schemas.js +20 -0
  59. package/build/actions/list-schemas.js.map +1 -0
  60. package/build/actions/{specs.d.ts → list-specification-revisions.d.ts} +1 -3
  61. package/build/actions/list-specification-revisions.d.ts.map +1 -0
  62. package/build/actions/list-specification-revisions.js +10 -0
  63. package/build/actions/list-specification-revisions.js.map +1 -0
  64. package/build/actions/summarize-cards.d.ts +6 -0
  65. package/build/actions/summarize-cards.d.ts.map +1 -0
  66. package/build/actions/summarize-cards.js +11 -0
  67. package/build/actions/summarize-cards.js.map +1 -0
  68. package/build/actions/transitions.d.ts +3 -3
  69. package/build/actions/transitions.d.ts.map +1 -1
  70. package/build/actions/transitions.js +4 -3
  71. package/build/actions/transitions.js.map +1 -1
  72. package/build/actions/types.d.ts +5 -3
  73. package/build/actions/types.d.ts.map +1 -1
  74. package/build/actions/watch.d.ts +2 -2
  75. package/build/actions/watch.d.ts.map +1 -1
  76. package/build/actions/watch.js +2 -2
  77. package/build/actions/watch.js.map +1 -1
  78. package/build/conformance/assert/types.d.ts +19 -0
  79. package/build/conformance/assert/types.d.ts.map +1 -0
  80. package/build/conformance/assert/types.js +2 -0
  81. package/build/conformance/assert/types.js.map +1 -0
  82. package/build/conformance/assert.d.ts +8 -13
  83. package/build/conformance/assert.d.ts.map +1 -1
  84. package/build/conformance/assert.js +41 -43
  85. package/build/conformance/assert.js.map +1 -1
  86. package/build/conformance/cases.d.ts.map +1 -1
  87. package/build/conformance/cases.js +86 -67
  88. package/build/conformance/cases.js.map +1 -1
  89. package/build/conformance/clock.d.ts +6 -0
  90. package/build/conformance/clock.d.ts.map +1 -0
  91. package/build/conformance/clock.js +12 -0
  92. package/build/conformance/clock.js.map +1 -0
  93. package/build/conformance/consts.d.ts +3 -1
  94. package/build/conformance/consts.d.ts.map +1 -1
  95. package/build/conformance/consts.js +84 -1
  96. package/build/conformance/consts.js.map +1 -1
  97. package/build/conformance/errors.d.ts +5 -0
  98. package/build/conformance/errors.d.ts.map +1 -0
  99. package/build/conformance/errors.js +8 -0
  100. package/build/conformance/errors.js.map +1 -0
  101. package/build/conformance/fixtures/types.d.ts +13 -0
  102. package/build/conformance/fixtures/types.d.ts.map +1 -0
  103. package/build/conformance/fixtures/types.js +2 -0
  104. package/build/conformance/fixtures/types.js.map +1 -0
  105. package/build/conformance/fixtures.d.ts +8 -8
  106. package/build/conformance/fixtures.d.ts.map +1 -1
  107. package/build/conformance/fixtures.js +24 -107
  108. package/build/conformance/fixtures.js.map +1 -1
  109. package/build/conformance/index.d.ts +3 -0
  110. package/build/conformance/index.d.ts.map +1 -1
  111. package/build/conformance/index.js +1 -0
  112. package/build/conformance/index.js.map +1 -1
  113. package/build/definitions.d.ts.map +1 -1
  114. package/build/definitions.js +9 -9
  115. package/build/definitions.js.map +1 -1
  116. package/build/executor/changes/types.d.ts +36 -0
  117. package/build/executor/changes/types.d.ts.map +1 -0
  118. package/build/executor/changes/types.js +2 -0
  119. package/build/executor/changes/types.js.map +1 -0
  120. package/build/executor/changes.d.ts +3 -30
  121. package/build/executor/changes.d.ts.map +1 -1
  122. package/build/executor/changes.js +42 -51
  123. package/build/executor/changes.js.map +1 -1
  124. package/build/executor/code/types.d.ts +17 -0
  125. package/build/executor/code/types.d.ts.map +1 -0
  126. package/build/executor/code/types.js +2 -0
  127. package/build/executor/code/types.js.map +1 -0
  128. package/build/executor/code.d.ts +3 -14
  129. package/build/executor/code.d.ts.map +1 -1
  130. package/build/executor/code.js +50 -54
  131. package/build/executor/code.js.map +1 -1
  132. package/build/executor/creator/types.d.ts +33 -0
  133. package/build/executor/creator/types.d.ts.map +1 -0
  134. package/build/executor/creator/types.js +2 -0
  135. package/build/executor/creator/types.js.map +1 -0
  136. package/build/executor/creator.d.ts +6 -25
  137. package/build/executor/creator.d.ts.map +1 -1
  138. package/build/executor/creator.js +26 -41
  139. package/build/executor/creator.js.map +1 -1
  140. package/build/executor/resolve/types.d.ts +30 -0
  141. package/build/executor/resolve/types.d.ts.map +1 -0
  142. package/build/executor/resolve/types.js +2 -0
  143. package/build/executor/resolve/types.js.map +1 -0
  144. package/build/executor/resolve.d.ts +4 -41
  145. package/build/executor/resolve.d.ts.map +1 -1
  146. package/build/executor/resolve.js +142 -157
  147. package/build/executor/resolve.js.map +1 -1
  148. package/build/executor/types.d.ts +0 -2
  149. package/build/executor/types.d.ts.map +1 -1
  150. package/build/executor/validate.d.ts +1 -1
  151. package/build/executor/validate.d.ts.map +1 -1
  152. package/build/executor/validate.js +16 -15
  153. package/build/executor/validate.js.map +1 -1
  154. package/build/executor.d.ts +4 -3
  155. package/build/executor.d.ts.map +1 -1
  156. package/build/executor.js +18 -17
  157. package/build/executor.js.map +1 -1
  158. package/build/facade.d.ts.map +1 -1
  159. package/build/facade.js +16 -16
  160. package/build/facade.js.map +1 -1
  161. package/build/helper.d.ts +1 -6
  162. package/build/helper.d.ts.map +1 -1
  163. package/build/helper.js +3 -8
  164. package/build/helper.js.map +1 -1
  165. package/build/projection.d.ts +2 -0
  166. package/build/projection.d.ts.map +1 -1
  167. package/build/projection.js +6 -4
  168. package/build/projection.js.map +1 -1
  169. package/build/queue/declare.d.ts +5 -0
  170. package/build/queue/declare.d.ts.map +1 -0
  171. package/build/queue/declare.js +5 -0
  172. package/build/queue/declare.js.map +1 -0
  173. package/build/queue/index.d.ts +1 -4
  174. package/build/queue/index.d.ts.map +1 -1
  175. package/build/queue/index.js +1 -4
  176. package/build/queue/index.js.map +1 -1
  177. package/build/store/composite.d.ts.map +1 -1
  178. package/build/store/composite.js +2 -2
  179. package/build/store/composite.js.map +1 -1
  180. package/build/store/fold/types.d.ts +43 -0
  181. package/build/store/fold/types.d.ts.map +1 -0
  182. package/build/store/fold/types.js +2 -0
  183. package/build/store/fold/types.js.map +1 -0
  184. package/build/store/fold.d.ts +7 -30
  185. package/build/store/fold.d.ts.map +1 -1
  186. package/build/store/fold.js +185 -203
  187. package/build/store/fold.js.map +1 -1
  188. package/build/store/index.d.ts +1 -0
  189. package/build/store/index.d.ts.map +1 -1
  190. package/build/store/index.js.map +1 -1
  191. package/build/store/memory.d.ts.map +1 -1
  192. package/build/store/memory.js +0 -0
  193. package/build/store/memory.js.map +1 -1
  194. package/build/store/types.local.d.ts +3 -1
  195. package/build/store/types.local.d.ts.map +1 -1
  196. package/build/types.d.ts +2 -1
  197. package/build/types.d.ts.map +1 -1
  198. package/build/utils/access/types.d.ts +30 -0
  199. package/build/utils/access/types.d.ts.map +1 -0
  200. package/build/utils/access/types.js +2 -0
  201. package/build/utils/access/types.js.map +1 -0
  202. package/build/utils/access.d.ts +4 -0
  203. package/build/utils/access.d.ts.map +1 -0
  204. package/build/utils/access.js +28 -0
  205. package/build/utils/access.js.map +1 -0
  206. package/build/utils/guard/types.d.ts +21 -0
  207. package/build/utils/guard/types.d.ts.map +1 -0
  208. package/build/utils/guard/types.js +2 -0
  209. package/build/utils/guard/types.js.map +1 -0
  210. package/build/utils/guard.d.ts +7 -10
  211. package/build/utils/guard.d.ts.map +1 -1
  212. package/build/utils/guard.js +33 -31
  213. package/build/utils/guard.js.map +1 -1
  214. package/build/utils/handler/types.d.ts +26 -0
  215. package/build/utils/handler/types.d.ts.map +1 -0
  216. package/build/utils/handler/types.js +2 -0
  217. package/build/utils/handler/types.js.map +1 -0
  218. package/build/utils/handler.d.ts +6 -0
  219. package/build/utils/handler.d.ts.map +1 -0
  220. package/build/utils/handler.js +21 -0
  221. package/build/utils/handler.js.map +1 -0
  222. package/build/utils/index.d.ts +8 -1
  223. package/build/utils/index.d.ts.map +1 -1
  224. package/build/utils/index.js +3 -0
  225. package/build/utils/index.js.map +1 -1
  226. package/build/utils/request/types.d.ts +23 -0
  227. package/build/utils/request/types.d.ts.map +1 -0
  228. package/build/utils/request/types.js +2 -0
  229. package/build/utils/request/types.js.map +1 -0
  230. package/build/utils/request.d.ts +4 -0
  231. package/build/utils/request.d.ts.map +1 -0
  232. package/build/utils/request.js +27 -0
  233. package/build/utils/request.js.map +1 -0
  234. package/build/utils/scope.d.ts +10 -43
  235. package/build/utils/scope.d.ts.map +1 -1
  236. package/build/utils/scope.js +21 -99
  237. package/build/utils/scope.js.map +1 -1
  238. package/package.json +18 -18
  239. package/src/actions/apply-schema-request.ts +35 -0
  240. package/src/actions/commit.ts +7 -5
  241. package/src/actions/define-schemas.ts +34 -0
  242. package/src/actions/execute.ts +30 -115
  243. package/src/actions/execution/types.ts +34 -0
  244. package/src/actions/execution.ts +96 -0
  245. package/src/actions/get-card.ts +14 -0
  246. package/src/actions/get-specification.ts +14 -0
  247. package/src/actions/index.ts +13 -3
  248. package/src/actions/links.ts +8 -6
  249. package/src/actions/list-card-specifications.ts +15 -0
  250. package/src/actions/list-card-transitions.ts +15 -0
  251. package/src/actions/list-cards.ts +14 -0
  252. package/src/actions/list-schemas.ts +29 -0
  253. package/src/actions/{specs.ts → list-specification-revisions.ts} +4 -11
  254. package/src/actions/summarize-cards.ts +15 -0
  255. package/src/actions/transitions.ts +6 -4
  256. package/src/actions/types.ts +4 -3
  257. package/src/actions/watch.ts +4 -3
  258. package/src/conformance/assert/types.ts +16 -0
  259. package/src/conformance/assert.ts +49 -41
  260. package/src/conformance/cases.ts +88 -67
  261. package/src/conformance/clock.ts +11 -0
  262. package/src/conformance/consts.ts +87 -1
  263. package/src/conformance/errors.ts +7 -0
  264. package/src/conformance/fixtures/types.ts +11 -0
  265. package/src/conformance/fixtures.ts +32 -107
  266. package/src/conformance/index.ts +3 -0
  267. package/src/definitions.ts +9 -9
  268. package/src/executor/changes/types.ts +38 -0
  269. package/src/executor/changes.ts +48 -63
  270. package/src/executor/code/types.ts +23 -0
  271. package/src/executor/code.ts +60 -61
  272. package/src/executor/creator/types.ts +33 -0
  273. package/src/executor/creator.ts +33 -41
  274. package/src/executor/resolve/types.ts +30 -0
  275. package/src/executor/resolve.ts +153 -167
  276. package/src/executor/types.ts +0 -3
  277. package/src/executor/validate.ts +17 -16
  278. package/src/executor.ts +20 -17
  279. package/src/facade.ts +16 -16
  280. package/src/helper.ts +3 -8
  281. package/src/projection.ts +8 -4
  282. package/src/queue/declare.ts +7 -0
  283. package/src/queue/index.ts +1 -8
  284. package/src/store/composite.ts +2 -2
  285. package/src/store/fold/types.ts +44 -0
  286. package/src/store/fold.ts +209 -212
  287. package/src/store/index.ts +1 -0
  288. package/src/store/memory.ts +0 -0
  289. package/src/store/types.local.ts +1 -1
  290. package/src/types.ts +1 -1
  291. package/src/utils/access/types.ts +30 -0
  292. package/src/utils/access.ts +33 -0
  293. package/src/utils/guard/types.ts +19 -0
  294. package/src/utils/guard.ts +41 -30
  295. package/src/utils/handler/types.ts +28 -0
  296. package/src/utils/handler.ts +35 -0
  297. package/src/utils/index.ts +9 -1
  298. package/src/utils/request/types.ts +23 -0
  299. package/src/utils/request.ts +40 -0
  300. package/src/utils/scope.ts +25 -108
  301. package/tests/creator.spec.ts +18 -18
  302. package/build/actions/cards.d.ts +0 -14
  303. package/build/actions/cards.d.ts.map +0 -1
  304. package/build/actions/cards.js +0 -32
  305. package/build/actions/cards.js.map +0 -1
  306. package/build/actions/schemas.d.ts +0 -28
  307. package/build/actions/schemas.d.ts.map +0 -1
  308. package/build/actions/schemas.js +0 -69
  309. package/build/actions/schemas.js.map +0 -1
  310. package/build/actions/specs.d.ts.map +0 -1
  311. package/build/actions/specs.js +0 -14
  312. package/build/actions/specs.js.map +0 -1
  313. package/src/actions/cards.ts +0 -48
  314. package/src/actions/schemas.ts +0 -87
@@ -1,7 +1,9 @@
1
- import { CardTypeNotAllowed, isProject, normalizeParents, ParentNotFound, PlanningError, PlanningSchemaKind, PlanningScopeMismatch, primaryFlowOf, projectOf, SchemaOrigin, slotOf, TransitionAction, UnknownStatusFlow, UnknownWorkcardType, WorkcardKind, WorkcardNotFound, type AnyTypeSchema, type PlanningFacade, type PlanningSchemaRegistry, type ProjectTypeSchema, type ScopedSchemaRegistry, type Specification, type TransitionExecution, type Workcard, type WorkcardDraft } from '@owlmeans/planning'
1
+ import { cardHelper, CardTypeNotAllowed, ParentNotFound, PlanningError, PlanningSchemaKind, PlanningScopeMismatch, SchemaOrigin, specificationHelper, statusHelper, TransitionAction, UnknownStatusFlow, UnknownWorkcardType, WorkcardKind, WorkcardNotFound, type AnyTypeSchema, type PlanningFacade, type PlanningSchemaRegistry, type ProjectTypeSchema, type ScopedSchemaRegistry, type Specification, type TransitionExecution, type Workcard, type WorkcardDraft } from '@owlmeans/planning'
2
+ import { memoHelper } from '@owlmeans/context'
2
3
  import type { PlanningRuntime } from '../types.js'
3
4
  import { TRIMMED } from './consts.local.js'
4
5
  import type { Resolved } from './types.js'
6
+ import type { ResolveUtils } from './resolve/types.js'
5
7
 
6
8
  const isScoped = (schemas: PlanningSchemaRegistry): schemas is ScopedSchemaRegistry =>
7
9
  typeof (schemas as Partial<ScopedSchemaRegistry>).originOf === 'function'
@@ -30,189 +32,173 @@ const trim = <T extends object>(record: T | undefined): T | undefined => {
30
32
  return copy as T
31
33
  }
32
34
 
33
- /**
34
- * Step 1: a deep copy with trimmed text, `parents ∋ parent` on a draft, and no duplicate `unset`
35
- * paths. The caller's object is never touched.
36
- */
37
- const normalizeExecution = (input: TransitionExecution): TransitionExecution => {
38
- const exec = structuredClone(input) as TransitionExecution
39
- if (exec.card != null && typeof exec.card === 'object') {
40
- const draft = trim(exec.card) as WorkcardDraft
41
- const parents = normalizeParents(draft.parent, draft.parents)
42
- exec.card = { ...draft, ...(parents.length > 0 ? { parents, parent: draft.parent ?? parents[0] } : {}) }
43
- }
44
- if (exec.changes != null) {
45
- exec.changes = trim(exec.changes)
46
- }
47
- if (exec.unset != null) {
48
- exec.unset = [...new Set(exec.unset)]
35
+ export const makeResolveUtils = (runtime: PlanningRuntime): ResolveUtils => {
36
+ const normalizeExecution = (input: TransitionExecution): TransitionExecution => {
37
+ const exec = structuredClone(input) as TransitionExecution
38
+ if (exec.card != null && typeof exec.card === 'object') {
39
+ const draft = trim(exec.card) as WorkcardDraft
40
+ const parents = cardHelper.normalizeParents(draft.parent, draft.parents)
41
+ exec.card = { ...draft, ...(parents.length > 0 ? { parents, parent: draft.parent ?? parents[0] } : {}) }
42
+ }
43
+ if (exec.changes != null) {
44
+ exec.changes = trim(exec.changes)
45
+ }
46
+ if (exec.unset != null) {
47
+ exec.unset = [...new Set(exec.unset)]
48
+ }
49
+ return exec
49
50
  }
50
- return exec
51
- }
52
51
 
53
- /**
54
- * The parent type's rule for a child of `kind`/`type`: a project lists the `cardTypes` (and
55
- * `projectTypes`) it admits, and one declaring `scopedCardTypes` also admits a card type its layer
56
- * (`view`, the parent project's resolved schemas) defines as data. Project types are code's alone.
57
- *
58
- * @throws {CardTypeNotAllowed}
59
- */
60
- const assertChildAllowed = (
61
- runtime: PlanningRuntime, parent: Workcard, kind: WorkcardKind, type: string, view?: PlanningSchemaRegistry
62
- ): void => {
63
- if (kind === WorkcardKind.Specification) {
64
- return
65
- }
66
- if (!isProject(parent)) {
52
+ const assertChildAllowed = (
53
+ parent: Workcard, kind: WorkcardKind, type: string, view?: PlanningSchemaRegistry
54
+ ): void => {
55
+ if (kind === WorkcardKind.Specification) {
56
+ return
57
+ }
58
+ if (!cardHelper.isProject(parent)) {
59
+ throw new CardTypeNotAllowed(`${parent.type}:${type}`)
60
+ }
61
+ const parentType = runtime.service().schemas.type(parent.type) as ProjectTypeSchema
62
+ const allowed = kind === WorkcardKind.Project ? parentType.projectTypes ?? [] : parentType.cardTypes ?? []
63
+ if (allowed.includes(type)) {
64
+ return
65
+ }
66
+ if (kind === WorkcardKind.Card && parentType.scopedCardTypes === true && dataDefined(view, type)) {
67
+ return
68
+ }
67
69
  throw new CardTypeNotAllowed(`${parent.type}:${type}`)
68
70
  }
69
- const parentType = runtime.service().schemas.type(parent.type) as ProjectTypeSchema
70
- const allowed = kind === WorkcardKind.Project ? parentType.projectTypes ?? [] : parentType.cardTypes ?? []
71
- if (allowed.includes(type)) {
72
- return
73
- }
74
- if (kind === WorkcardKind.Card && parentType.scopedCardTypes === true && dataDefined(view, type)) {
75
- return
76
- }
77
- throw new CardTypeNotAllowed(`${parent.type}:${type}`)
78
- }
79
71
 
80
- /** The layer a parent project admits data-defined children through — `undefined` without a schema port. */
81
- const childViewOf = async (
82
- runtime: PlanningRuntime, entityId: string, parent: Workcard
83
- ): Promise<PlanningSchemaRegistry | undefined> =>
84
- runtime.schemaStore() == null || !isProject(parent) ? undefined : await runtime.schemasFor(entityId, parent.id)
85
-
86
- /**
87
- * Step 4: the card, its type and flow, its parent and — for a specification — its slot.
88
- *
89
- * @throws {WorkcardNotFound | PlanningScopeMismatch | UnknownWorkcardType | UnknownStatusFlow}
90
- * @throws {ParentNotFound | CardTypeNotAllowed | PlanningError}
91
- */
92
- const resolveExecution = async (
93
- runtime: PlanningRuntime, facade: PlanningFacade, exec: TransitionExecution
94
- ): Promise<Resolved> => {
95
- const service = runtime.service()
96
- const entityId = facade.scope.entityId
97
- const create = exec.action === TransitionAction.Create
98
- // Without a schema port every step reads the code registry itself, in today's order; with one,
99
- // the card's project is found first and the step reads that project's resolved layer.
100
- const layered = runtime.schemaStore() != null
101
- let schemas: PlanningSchemaRegistry = service.schemas
102
-
103
- let card: Workcard | undefined
104
- let type: AnyTypeSchema
105
- let parent: Workcard | undefined
106
-
107
- if (create) {
108
- if (exec.card == null || typeof exec.card !== 'object') {
109
- throw new PlanningError('malformed:create-without-draft')
110
- }
111
- const draft = exec.card
112
- const parents = normalizeParents(draft.parent, draft.parents)
113
- const loaded = new Map<string, Workcard>()
114
- if (layered) {
115
- // Loaded through the facade, so a parent this scope cannot see is absent here too.
116
- const primary = parents[0] != null ? await facade.cards.load(parents[0]) : null
117
- if (parents[0] != null && primary == null) {
118
- throw new ParentNotFound(parents[0])
72
+ const childViewOf = async (
73
+ entityId: string, parent: Workcard
74
+ ): Promise<PlanningSchemaRegistry | undefined> =>
75
+ runtime.schemaStore() == null || !cardHelper.isProject(parent) ? undefined : await runtime.schemasFor(entityId, parent.id)
76
+
77
+ const resolveExecution = async (
78
+ facade: PlanningFacade, exec: TransitionExecution
79
+ ): Promise<Resolved> => {
80
+ const service = runtime.service()
81
+ const entityId = facade.scope.entityId
82
+ const create = exec.action === TransitionAction.Create
83
+ // Without a schema port every step reads the code registry itself, in today's order; with one,
84
+ // the card's project is found first and the step reads that project's resolved layer.
85
+ const layered = runtime.schemaStore() != null
86
+ let schemas: PlanningSchemaRegistry = service.schemas
87
+
88
+ let card: Workcard | undefined
89
+ let type: AnyTypeSchema
90
+ let parent: Workcard | undefined
91
+
92
+ if (create) {
93
+ if (exec.card == null || typeof exec.card !== 'object') {
94
+ throw new PlanningError('malformed:create-without-draft')
119
95
  }
120
- if (primary != null) {
121
- loaded.set(parents[0], primary)
96
+ const draft = exec.card
97
+ const parents = cardHelper.normalizeParents(draft.parent, draft.parents)
98
+ const loaded = new Map<string, Workcard>()
99
+ if (layered) {
100
+ // Loaded through the facade, so a parent this scope cannot see is absent here too.
101
+ const primary = parents[0] != null ? await facade.cards.load(parents[0]) : null
102
+ if (parents[0] != null && primary == null) {
103
+ throw new ParentNotFound(parents[0])
104
+ }
105
+ if (primary != null) {
106
+ loaded.set(parents[0], primary)
107
+ }
108
+ const project = draft.kind === WorkcardKind.Project || primary == null
109
+ ? undefined
110
+ : cardHelper.projectOf({ kind: draft.kind, parent: parents[0], parents } as unknown as Workcard, primary)
111
+ schemas = await runtime.schemasFor(entityId, project)
122
112
  }
123
- const project = draft.kind === WorkcardKind.Project || primary == null
124
- ? undefined
125
- : projectOf({ kind: draft.kind, parent: parents[0], parents } as unknown as Workcard, primary)
126
- schemas = await runtime.schemasFor(entityId, project)
127
- }
128
- type = schemas.type(draft.type)
129
- if (type.kind !== draft.kind) {
130
- throw new PlanningError(`malformed:kind:${draft.type}:${draft.kind}`)
131
- }
132
- if (layered && isScoped(schemas) && schemas.isRetired(PlanningSchemaKind.Type, draft.type)) {
133
- // Existing cards keep resolving a retired type; nothing new is made of it.
134
- throw new UnknownWorkcardType(`retired:${draft.type}`)
135
- }
136
- // Normalized: the primary parent is first. It must accept the child; so must every project
137
- // among the secondary parents.
138
- for (const [index, id] of parents.entries()) {
139
- const found = loaded.get(id) ?? await facade.cards.load(id)
113
+ type = schemas.type(draft.type)
114
+ if (type.kind !== draft.kind) {
115
+ throw new PlanningError(`malformed:kind:${draft.type}:${draft.kind}`)
116
+ }
117
+ if (layered && isScoped(schemas) && schemas.isRetired(PlanningSchemaKind.Type, draft.type)) {
118
+ // Existing cards keep resolving a retired type; nothing new is made of it.
119
+ throw new UnknownWorkcardType(`retired:${draft.type}`)
120
+ }
121
+ // Normalized: the primary parent is first. It must accept the child; so must every project
122
+ // among the secondary parents.
123
+ for (const [index, id] of parents.entries()) {
124
+ const found = loaded.get(id) ?? await facade.cards.load(id)
125
+ if (found == null) {
126
+ throw new ParentNotFound(id)
127
+ }
128
+ if (index === 0) {
129
+ parent = found
130
+ }
131
+ if (index === 0 || cardHelper.isProject(found)) {
132
+ const view = !layered ? undefined : index === 0 && cardHelper.isProject(found) ? schemas : await childViewOf(entityId, found)
133
+ assertChildAllowed(found, draft.kind, draft.type, view)
134
+ }
135
+ }
136
+ if (draft.kind === WorkcardKind.Specification && parent == null) {
137
+ throw new PlanningError('malformed:specification-without-parent')
138
+ }
139
+ } else {
140
+ if (typeof exec.card !== 'string' || exec.card === '') {
141
+ throw new PlanningError(`malformed:${exec.action}-without-card`)
142
+ }
143
+ const found = await runtime.reader().cards.get(exec.card, entityId)
140
144
  if (found == null) {
141
- throw new ParentNotFound(id)
145
+ throw new WorkcardNotFound(exec.card)
142
146
  }
143
- if (index === 0) {
144
- parent = found
147
+ if (found.entityId !== entityId) {
148
+ throw new PlanningScopeMismatch(exec.card)
145
149
  }
146
- if (index === 0 || isProject(found)) {
147
- const view = !layered ? undefined : index === 0 && isProject(found) ? schemas : await childViewOf(runtime, entityId, found)
148
- assertChildAllowed(runtime, found, draft.kind, draft.type, view)
150
+ if (facade.scope.projects != null && await facade.cards.load(exec.card) == null) {
151
+ // Outside the scope's projects: absent, exactly like another entity's card.
152
+ throw new WorkcardNotFound(exec.card)
153
+ }
154
+ card = found
155
+ if (layered) {
156
+ parent = card.parent != null ? await facade.cards.load(card.parent) ?? undefined : undefined
157
+ schemas = await runtime.schemasFor(entityId, cardHelper.projectOf(card, parent))
158
+ type = schemas.type(card.type)
159
+ } else {
160
+ type = schemas.type(card.type)
161
+ parent = card.parent != null ? await facade.cards.load(card.parent) ?? undefined : undefined
149
162
  }
150
163
  }
151
- if (draft.kind === WorkcardKind.Specification && parent == null) {
152
- throw new PlanningError('malformed:specification-without-parent')
153
- }
154
- } else {
155
- if (typeof exec.card !== 'string' || exec.card === '') {
156
- throw new PlanningError(`malformed:${exec.action}-without-card`)
157
- }
158
- const found = await runtime.reader().cards.get(exec.card, entityId)
159
- if (found == null) {
160
- throw new WorkcardNotFound(exec.card)
161
- }
162
- if (found.entityId !== entityId) {
163
- throw new PlanningScopeMismatch(exec.card)
164
- }
165
- if (facade.scope.projects != null && await facade.cards.load(exec.card) == null) {
166
- // Outside the scope's projects: absent, exactly like another entity's card.
167
- throw new WorkcardNotFound(exec.card)
168
- }
169
- card = found
170
- if (layered) {
171
- parent = card.parent != null ? await facade.cards.load(card.parent) ?? undefined : undefined
172
- schemas = await runtime.schemasFor(entityId, projectOf(card, parent))
173
- type = schemas.type(card.type)
174
- } else {
175
- type = schemas.type(card.type)
176
- parent = card.parent != null ? await facade.cards.load(card.parent) ?? undefined : undefined
164
+
165
+ const flowId = exec.flow ?? statusHelper.primaryFlowOf(type)
166
+ if (!type.flows.includes(flowId)) {
167
+ throw new UnknownStatusFlow(`${type.type}:${flowId}`)
177
168
  }
178
- }
179
169
 
180
- const flowId = exec.flow ?? primaryFlowOf(type)
181
- if (!type.flows.includes(flowId)) {
182
- throw new UnknownStatusFlow(`${type.type}:${flowId}`)
170
+ const kind = create ? (exec.card as WorkcardDraft).kind : card!.kind
171
+ const category = create ? (exec.card as WorkcardDraft).category : (card as Specification | undefined)?.category
172
+ const slot = kind === WorkcardKind.Specification && parent != null && category != null
173
+ ? specificationHelper.slotOf(schemas.type(parent.type), category)
174
+ : undefined
175
+
176
+ return {
177
+ create,
178
+ ...(card != null ? { card } : {}),
179
+ type,
180
+ flowId,
181
+ flow: schemas.flow(flowId),
182
+ ...(parent != null ? { parent } : {}),
183
+ ...(slot != null ? { slot } : {}),
184
+ store: service.store(type.type),
185
+ schemas,
186
+ }
183
187
  }
184
188
 
185
- const kind = create ? (exec.card as WorkcardDraft).kind : card!.kind
186
- const category = create ? (exec.card as WorkcardDraft).category : (card as Specification | undefined)?.category
187
- const slot = kind === WorkcardKind.Specification && parent != null && category != null
188
- ? slotOf(schemas.type(parent.type), category)
189
- : undefined
190
-
191
- return {
192
- create,
193
- ...(card != null ? { card } : {}),
194
- type,
195
- flowId,
196
- flow: schemas.flow(flowId),
197
- ...(parent != null ? { parent } : {}),
198
- ...(slot != null ? { slot } : {}),
199
- store: service.store(type.type),
200
- schemas,
189
+ const projectFor = (resolved: Resolved, exec: TransitionExecution, cardId: string): string | undefined => {
190
+ if (resolved.card != null) {
191
+ return cardHelper.isProject(resolved.card) ? resolved.card.id : cardHelper.projectOf(resolved.card, resolved.parent)
192
+ }
193
+ const draft = exec.card as WorkcardDraft
194
+ if (draft.kind === WorkcardKind.Project) {
195
+ return cardId
196
+ }
197
+ return cardHelper.projectOf({ kind: draft.kind, parent: draft.parent, parents: draft.parents ?? [] } as unknown as Workcard, resolved.parent)
201
198
  }
202
- }
203
199
 
204
- /** The project a transition is filed under: a project's own id, else its parent's project. */
205
- const projectFor = (resolved: Resolved, exec: TransitionExecution, cardId: string): string | undefined => {
206
- if (resolved.card != null) {
207
- return isProject(resolved.card) ? resolved.card.id : projectOf(resolved.card, resolved.parent)
208
- }
209
- const draft = exec.card as WorkcardDraft
210
- if (draft.kind === WorkcardKind.Project) {
211
- return cardId
212
- }
213
- return projectOf({ kind: draft.kind, parent: draft.parent, parents: draft.parents ?? [] } as unknown as Workcard, resolved.parent)
200
+ return { normalizeExecution, assertChildAllowed, childViewOf, resolveExecution, projectFor }
214
201
  }
215
202
 
216
- export const createResolveHelper = () => ({ normalizeExecution, assertChildAllowed, childViewOf, resolveExecution, projectFor })
217
-
218
- export const resolveHelper = createResolveHelper()
203
+ /** The resolution steps of one service runtime — one per runtime. */
204
+ export const resolveUtilsOf = memoHelper.oncePer(makeResolveUtils)
@@ -1,5 +1,4 @@
1
1
  import type { AnyTypeSchema, PlanningSchemaRegistry, PlanningStore, SpecificationSlot, StatusFlowSchema, Workcard } from '@owlmeans/planning'
2
- import type { createResolveHelper } from './resolve.js'
3
2
 
4
3
  /** Everything the steps after resolution read about an execution. */
5
4
  export interface Resolved {
@@ -21,5 +20,3 @@ export interface Resolved {
21
20
  */
22
21
  schemas: PlanningSchemaRegistry
23
22
  }
24
-
25
- export type ResolveHelper = ReturnType<typeof createResolveHelper>
@@ -1,9 +1,9 @@
1
- import { applyUnset, assertMutable, BODY_MAX, CODE_MAX, DESCRIPTION_MAX, IllegalTransition, LabelNotAllowed, MAX_LABELS, MAX_PARENTS, mergeFields, normalizeParents, ParentNotFound, PlanningError, RelationshipRefused, ruleOf, SpecificationRevisionConflict, SpecificationSlotUnknown, slotOf, TITLE_MAX, TransitionAction, validateFields, validateSpecificationBody, WorkcardKind, type PlanningFacade, type RelationshipDraft, type Specification, type TransitionExecution, type WorkcardDraft } from '@owlmeans/planning'
1
+ import { BODY_MAX, cardHelper, changesHelper, CODE_MAX, DESCRIPTION_MAX, IllegalTransition, LabelNotAllowed, MAX_LABELS, MAX_PARENTS, ParentNotFound, PlanningError, RelationshipRefused, specificationHelper, SpecificationRevisionConflict, SpecificationSlotUnknown, statusHelper, TITLE_MAX, TransitionAction, validateHelper, WorkcardKind, type PlanningFacade, type RelationshipDraft, type Specification, type TransitionExecution, type WorkcardDraft } from '@owlmeans/planning'
2
2
  import type { PlanningRuntime } from '../types.js'
3
- import { assertCreatorFixed } from './creator.js'
3
+ import { creatorHelper } from './creator.js'
4
4
  import type { Resolved } from './types.js'
5
5
  import { ACTIONS } from './consts.local.js'
6
- import { resolveHelper } from './resolve.js'
6
+ import { resolveUtilsOf } from './resolve.js'
7
7
 
8
8
  const malformed = (what: string): PlanningError => new PlanningError(`malformed:${what}`)
9
9
 
@@ -64,7 +64,7 @@ const assertFlow = (exec: TransitionExecution, resolved: Resolved): void => {
64
64
  }
65
65
  const card = resolved.card!
66
66
  const from = card.flows[resolved.flowId] ?? card.status
67
- if (ruleOf(resolved.flow, exec.transition!, from) == null) {
67
+ if (statusHelper.ruleOf(resolved.flow, exec.transition!, from) == null) {
68
68
  throw new IllegalTransition(`${resolved.flowId}:${exec.transition}:${from}`)
69
69
  }
70
70
  }
@@ -73,15 +73,15 @@ const assertFields = (exec: TransitionExecution, resolved: Resolved): void => {
73
73
  const schemas = resolved.schemas
74
74
  if (resolved.create) {
75
75
  const draft = exec.card as WorkcardDraft
76
- validateFields(schemas, resolved.type.type, mergeFields(draft.fields, exec.changes?.fields))
76
+ validateHelper.validateFields(schemas, resolved.type.type, changesHelper.mergeFields(draft.fields, exec.changes?.fields))
77
77
  return
78
78
  }
79
79
  const clears = (exec.unset ?? []).filter(path => path.startsWith('fields.'))
80
80
  if (exec.changes?.fields == null && clears.length === 0) {
81
81
  return
82
82
  }
83
- const merged = applyUnset({ fields: mergeFields(resolved.card!.fields, exec.changes?.fields) }, clears)
84
- validateFields(schemas, resolved.type.type, merged.fields)
83
+ const merged = changesHelper.applyUnset({ fields: changesHelper.mergeFields(resolved.card!.fields, exec.changes?.fields) }, clears)
84
+ validateHelper.validateFields(schemas, resolved.type.type, merged.fields)
85
85
  }
86
86
 
87
87
  const assertLabels = (exec: TransitionExecution, resolved: Resolved): void => {
@@ -109,7 +109,8 @@ const assertParentMove = async (
109
109
  return
110
110
  }
111
111
  const changes = exec.changes ?? {}
112
- const moved = [...new Set(normalizeParents(changes.parent, changes.parents))]
112
+ const resolve = resolveUtilsOf(runtime)
113
+ const moved = [...new Set(cardHelper.normalizeParents(changes.parent, changes.parents))]
113
114
  .filter(id => id !== resolved.card!.parent && !resolved.card!.parents.includes(id))
114
115
  for (const id of moved) {
115
116
  if (id === resolved.card!.id) {
@@ -119,8 +120,8 @@ const assertParentMove = async (
119
120
  if (found == null) {
120
121
  throw new ParentNotFound(id)
121
122
  }
122
- resolveHelper.assertChildAllowed(
123
- runtime, found, resolved.card!.kind, resolved.card!.type, await resolveHelper.childViewOf(runtime, facade.scope.entityId, found)
123
+ resolve.assertChildAllowed(
124
+ found, resolved.card!.kind, resolved.card!.type, await resolve.childViewOf(facade.scope.entityId, found)
124
125
  )
125
126
  }
126
127
  }
@@ -140,7 +141,7 @@ const assertSpecification = async (
140
141
  if (draft.category == null || draft.category === '') {
141
142
  throw malformed('specification-without-category')
142
143
  }
143
- const slot = slotOf(schemas.type(parent.type), draft.category)
144
+ const slot = specificationHelper.slotOf(schemas.type(parent.type), draft.category)
144
145
  if (slot == null) {
145
146
  throw new SpecificationSlotUnknown(`${parent.type}:${draft.category}`)
146
147
  }
@@ -148,7 +149,7 @@ const assertSpecification = async (
148
149
  throw malformed(`format:${draft.category}:${draft.format}`)
149
150
  }
150
151
  assertText('body', draft.body, BODY_MAX, false)
151
- validateSpecificationBody(slot, draft.body)
152
+ validateHelper.validateSpecificationBody(slot, draft.body)
152
153
  if (slot.multiple !== true && await facade.specifications.current(parent.id!, draft.category) != null) {
153
154
  throw new SpecificationRevisionConflict(`${parent.id}:${draft.category}`)
154
155
  }
@@ -169,7 +170,7 @@ const assertSpecification = async (
169
170
  }
170
171
  if (changes.body != null) {
171
172
  assertText('body', changes.body, BODY_MAX, false)
172
- validateSpecificationBody(slot, changes.body)
173
+ validateHelper.validateSpecificationBody(slot, changes.body)
173
174
  }
174
175
  }
175
176
 
@@ -226,7 +227,7 @@ const assertRelationships = async (
226
227
  }
227
228
 
228
229
  /**
229
- * Step 6: shape, flow rule, immutables (`createdBy` among them, {@link assertCreatorFixed}),
230
+ * Step 6: shape, flow rule, immutables (`createdBy` among them, `creatorHelper.assertCreatorFixed`),
230
231
  * `fields`, labels, parent moves, the specification slot and relationships — in that order.
231
232
  * Nothing is written by a refusal here.
232
233
  *
@@ -239,8 +240,8 @@ export const validateExecution = async (
239
240
  ): Promise<void> => {
240
241
  assertShape(exec, resolved)
241
242
  assertFlow(exec, resolved)
242
- assertMutable(exec, resolved.type)
243
- assertCreatorFixed(exec)
243
+ changesHelper.assertMutable(exec, resolved.type)
244
+ creatorHelper.assertCreatorFixed(exec)
244
245
  assertFields(exec, resolved)
245
246
  assertLabels(exec, resolved)
246
247
  await assertParentMove(exec, resolved, runtime, facade)
package/src/executor.ts CHANGED
@@ -1,16 +1,18 @@
1
- import { uuid } from '@owlmeans/basic-ids'
1
+
2
2
  import type { BasicConfig, BasicContext } from '@owlmeans/context'
3
3
  import { PlanningError, PlanningUnsupported, WorkcardConflict, type ExecuteOptions, type PlanningExecContext, type PlanningFacade, type PlanningPlugin, type PlanningStore, type Transition, type TransitionExecution, type TransitionReceipt } from '@owlmeans/planning'
4
- import { changeSetOf, transitionOf } from './executor/changes.js'
5
- import { assignCode } from './executor/code.js'
6
- import { withCreator } from './executor/creator.js'
4
+ import { changesUtils } from './executor/changes.js'
5
+ import { codeUtils } from './executor/code.js'
6
+ import { creatorHelper } from './executor/creator.js'
7
7
  import { makeReceipt } from './executor/receipt.js'
8
8
  import type { Resolved } from './executor/types.js'
9
9
  import { validateExecution } from './executor/validate.js'
10
10
  import type { PlanningRuntime } from './types.js'
11
- import { resolveHelper } from './executor/resolve.js'
11
+ import { resolveUtilsOf } from './executor/resolve.js'
12
+ import { idHelper } from '@owlmeans/basic-ids'
12
13
 
13
- export { assertCreatorFixed, creatorOf, withCreator } from './executor/creator.js'
14
+ export { assertCreatorFixed, createCreatorHelper, creatorHelper, creatorOf, withCreator } from './executor/creator.js'
15
+ export type * from './executor/creator/types.js'
14
16
 
15
17
  const isoNow = (): string => new Date().toISOString()
16
18
 
@@ -63,10 +65,10 @@ const currentReceipt = async (
63
65
  /**
64
66
  * THE write path. Nothing is appended before step 11, so every refusal leaves the log untouched.
65
67
  *
66
- * 1 normalize (a create's `createdBy` defaults to the scope's subject — {@link withCreator}) →
68
+ * 1 normalize (a create's `createdBy` defaults to the scope's subject — `creatorHelper.withCreator`) →
67
69
  * 2 scope → 3 idempotency (a known `key` answers its first receipt, before any validation) →
68
70
  * 4 resolve → 5 the plugins' `before` chain (re-resolved once when it moved the type or the parent)
69
- * → 6 validate (a `createdBy` in `changes`/`unset` is refused — {@link assertCreatorFixed}) →
71
+ * → 6 validate (a `createdBy` in `changes`/`unset` is refused — `creatorHelper.assertCreatorFixed`) →
70
72
  * 7 allocate the card id → 8 code → 9 changes (an update changing nothing answers
71
73
  * the current receipt and appends nothing) → 10 seq, a CAS against the head → 11 append →
72
74
  * 12 request the projection → 13 receipt → 14 `wait`.
@@ -78,8 +80,9 @@ export const executeTransition = async (
78
80
  ): Promise<TransitionReceipt> => {
79
81
  const scope = facade.scope
80
82
  const at = (runtime.options.now ?? isoNow)()
83
+ const resolve = resolveUtilsOf(runtime)
81
84
 
82
- let exec = withCreator(resolveHelper.normalizeExecution(input), scope)
85
+ let exec = creatorHelper.withCreator(resolve.normalizeExecution(input), scope)
83
86
 
84
87
  if (scope.entityId == null || scope.entityId === '') {
85
88
  throw new PlanningError('malformed:scope-without-entity')
@@ -92,12 +95,12 @@ export const executeTransition = async (
92
95
  }
93
96
  }
94
97
 
95
- let resolved = await resolveHelper.resolveExecution(runtime, facade, exec)
98
+ let resolved = await resolve.resolveExecution(facade, exec)
96
99
 
97
100
  const before = identityOf(exec)
98
- exec = resolveHelper.normalizeExecution(await runtime.registry.before(exec, execContextOf(runtime, facade, resolved)))
101
+ exec = resolve.normalizeExecution(await runtime.registry.before(exec, execContextOf(runtime, facade, resolved)))
99
102
  if (identityOf(exec) !== before) {
100
- resolved = await resolveHelper.resolveExecution(runtime, facade, exec)
103
+ resolved = await resolve.resolveExecution(facade, exec)
101
104
  }
102
105
 
103
106
  await validateExecution(runtime, facade, exec, resolved)
@@ -109,12 +112,12 @@ export const executeTransition = async (
109
112
  }
110
113
 
111
114
  const cardId = resolved.create
112
- ? store.newId?.() ?? runtime.options.ids?.() ?? uuid()
115
+ ? store.newId?.() ?? runtime.options.ids?.() ?? idHelper.uuid()
113
116
  : resolved.card!.id!
114
117
 
115
- exec = await assignCode(exec, resolved, scope, runtime.registry, execContextOf(runtime, facade, resolved))
118
+ exec = await codeUtils.assignCode(exec, resolved, scope, runtime.registry, execContextOf(runtime, facade, resolved))
116
119
 
117
- const { set, empty } = changeSetOf(exec, resolved, resolved.schemas, at)
120
+ const { set, empty } = changesUtils.changeSetOf(exec, resolved, resolved.schemas, at)
118
121
  if (empty) {
119
122
  return await currentReceipt(store, resolved, scope.entityId, opts)
120
123
  }
@@ -124,9 +127,9 @@ export const executeTransition = async (
124
127
  throw new WorkcardConflict(`${cardId}:create:${seq}`)
125
128
  }
126
129
 
127
- const transition = transitionOf({
130
+ const transition = changesUtils.transitionOf({
128
131
  exec, resolved, scope, set, cardId, seq, at,
129
- project: resolveHelper.projectFor(resolved, exec, cardId),
132
+ project: resolve.projectFor(resolved, exec, cardId),
130
133
  })
131
134
 
132
135
  let appended: Transition