@owlmeans/server-planning 0.1.18-rc.8 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (438) hide show
  1. package/README.md +92 -19
  2. package/agent-meta/manifest.json +105 -4
  3. package/agent-meta/skills/server-planning/SKILL.md +89 -138
  4. package/agent-meta/skills/server-planning/parts/auxiliary.md +54 -0
  5. package/agent-meta/skills/server-planning/parts/executor.md +82 -0
  6. package/agent-meta/skills/server-planning/parts/schemas.md +26 -0
  7. package/agent-meta/skills/server-planning/parts/serving.md +88 -0
  8. package/agent-meta/skills/server-planning/parts/stores.md +91 -0
  9. package/build/actions/apply-schema-request.d.ts +8 -0
  10. package/build/actions/apply-schema-request.d.ts.map +1 -0
  11. package/build/actions/apply-schema-request.js +33 -0
  12. package/build/actions/apply-schema-request.js.map +1 -0
  13. package/build/actions/commit.d.ts +3 -3
  14. package/build/actions/commit.d.ts.map +1 -1
  15. package/build/actions/commit.js +5 -4
  16. package/build/actions/commit.js.map +1 -1
  17. package/build/actions/define-schemas.d.ts +13 -0
  18. package/build/actions/define-schemas.d.ts.map +1 -0
  19. package/build/actions/define-schemas.js +28 -0
  20. package/build/actions/define-schemas.js.map +1 -0
  21. package/build/actions/execute.d.ts +11 -10
  22. package/build/actions/execute.d.ts.map +1 -1
  23. package/build/actions/execute.js +17 -27
  24. package/build/actions/execute.js.map +1 -1
  25. package/build/actions/execution/types.d.ts +34 -0
  26. package/build/actions/execution/types.d.ts.map +1 -0
  27. package/build/actions/execution/types.js +2 -0
  28. package/build/actions/execution/types.js.map +1 -0
  29. package/build/actions/execution.d.ts +4 -0
  30. package/build/actions/execution.d.ts.map +1 -0
  31. package/build/actions/execution.js +88 -0
  32. package/build/actions/execution.js.map +1 -0
  33. package/build/actions/get-card.d.ts +6 -0
  34. package/build/actions/get-card.d.ts.map +1 -0
  35. package/build/actions/get-card.js +9 -0
  36. package/build/actions/get-card.js.map +1 -0
  37. package/build/actions/get-specification.d.ts +6 -0
  38. package/build/actions/get-specification.d.ts.map +1 -0
  39. package/build/actions/get-specification.js +9 -0
  40. package/build/actions/get-specification.js.map +1 -0
  41. package/build/actions/index.d.ts +12 -3
  42. package/build/actions/index.d.ts.map +1 -1
  43. package/build/actions/index.js +11 -3
  44. package/build/actions/index.js.map +1 -1
  45. package/build/actions/links.d.ts +3 -3
  46. package/build/actions/links.d.ts.map +1 -1
  47. package/build/actions/links.js +6 -5
  48. package/build/actions/links.js.map +1 -1
  49. package/build/actions/list-card-specifications.d.ts +6 -0
  50. package/build/actions/list-card-specifications.d.ts.map +1 -0
  51. package/build/actions/list-card-specifications.js +11 -0
  52. package/build/actions/list-card-specifications.js.map +1 -0
  53. package/build/actions/list-card-transitions.d.ts +6 -0
  54. package/build/actions/list-card-transitions.d.ts.map +1 -0
  55. package/build/actions/list-card-transitions.js +11 -0
  56. package/build/actions/list-card-transitions.js.map +1 -0
  57. package/build/actions/list-cards.d.ts +6 -0
  58. package/build/actions/list-cards.d.ts.map +1 -0
  59. package/build/actions/list-cards.js +10 -0
  60. package/build/actions/list-cards.js.map +1 -0
  61. package/build/actions/list-schemas.d.ts +13 -0
  62. package/build/actions/list-schemas.d.ts.map +1 -0
  63. package/build/actions/list-schemas.js +20 -0
  64. package/build/actions/list-schemas.js.map +1 -0
  65. package/build/actions/list-specification-revisions.d.ts +6 -0
  66. package/build/actions/list-specification-revisions.d.ts.map +1 -0
  67. package/build/actions/list-specification-revisions.js +10 -0
  68. package/build/actions/list-specification-revisions.js.map +1 -0
  69. package/build/actions/resources.d.ts +5 -0
  70. package/build/actions/resources.d.ts.map +1 -0
  71. package/build/actions/resources.js +111 -0
  72. package/build/actions/resources.js.map +1 -0
  73. package/build/actions/summarize-cards.d.ts +6 -0
  74. package/build/actions/summarize-cards.d.ts.map +1 -0
  75. package/build/actions/summarize-cards.js +11 -0
  76. package/build/actions/summarize-cards.js.map +1 -0
  77. package/build/actions/transitions.d.ts +3 -3
  78. package/build/actions/transitions.d.ts.map +1 -1
  79. package/build/actions/transitions.js +4 -3
  80. package/build/actions/transitions.js.map +1 -1
  81. package/build/actions/types.d.ts +6 -0
  82. package/build/actions/types.d.ts.map +1 -0
  83. package/build/actions/types.js +2 -0
  84. package/build/actions/types.js.map +1 -0
  85. package/build/actions/watch.d.ts +2 -2
  86. package/build/actions/watch.d.ts.map +1 -1
  87. package/build/actions/watch.js +8 -6
  88. package/build/actions/watch.js.map +1 -1
  89. package/build/conformance/assert/types.d.ts +19 -0
  90. package/build/conformance/assert/types.d.ts.map +1 -0
  91. package/build/conformance/assert/types.js +2 -0
  92. package/build/conformance/assert/types.js.map +1 -0
  93. package/build/conformance/assert.d.ts +15 -0
  94. package/build/conformance/assert.d.ts.map +1 -0
  95. package/build/conformance/assert.js +52 -0
  96. package/build/conformance/assert.js.map +1 -0
  97. package/build/conformance/cases.d.ts +7 -0
  98. package/build/conformance/cases.d.ts.map +1 -0
  99. package/build/conformance/cases.js +544 -0
  100. package/build/conformance/cases.js.map +1 -0
  101. package/build/conformance/clock.d.ts +6 -0
  102. package/build/conformance/clock.d.ts.map +1 -0
  103. package/build/conformance/clock.js +12 -0
  104. package/build/conformance/clock.js.map +1 -0
  105. package/build/conformance/consts.d.ts +30 -0
  106. package/build/conformance/consts.d.ts.map +1 -0
  107. package/build/conformance/consts.js +159 -0
  108. package/build/conformance/consts.js.map +1 -0
  109. package/build/conformance/errors.d.ts +5 -0
  110. package/build/conformance/errors.d.ts.map +1 -0
  111. package/build/conformance/errors.js +8 -0
  112. package/build/conformance/errors.js.map +1 -0
  113. package/build/conformance/fixtures/types.d.ts +13 -0
  114. package/build/conformance/fixtures/types.d.ts.map +1 -0
  115. package/build/conformance/fixtures/types.js +2 -0
  116. package/build/conformance/fixtures/types.js.map +1 -0
  117. package/build/conformance/fixtures.d.ts +14 -0
  118. package/build/conformance/fixtures.d.ts.map +1 -0
  119. package/build/conformance/fixtures.js +26 -0
  120. package/build/conformance/fixtures.js.map +1 -0
  121. package/build/conformance/index.d.ts +15 -0
  122. package/build/conformance/index.d.ts.map +1 -0
  123. package/build/conformance/index.js +12 -0
  124. package/build/conformance/index.js.map +1 -0
  125. package/build/conformance/resources.d.ts +4 -0
  126. package/build/conformance/resources.d.ts.map +1 -0
  127. package/build/conformance/resources.js +228 -0
  128. package/build/conformance/resources.js.map +1 -0
  129. package/build/conformance/types.d.ts +18 -0
  130. package/build/conformance/types.d.ts.map +1 -0
  131. package/build/conformance/types.js +2 -0
  132. package/build/conformance/types.js.map +1 -0
  133. package/build/consts.d.ts +5 -0
  134. package/build/consts.d.ts.map +1 -1
  135. package/build/consts.js +5 -0
  136. package/build/consts.js.map +1 -1
  137. package/build/definitions.d.ts +18 -0
  138. package/build/definitions.d.ts.map +1 -0
  139. package/build/definitions.js +225 -0
  140. package/build/definitions.js.map +1 -0
  141. package/build/executor/changes/types.d.ts +36 -0
  142. package/build/executor/changes/types.d.ts.map +1 -0
  143. package/build/executor/changes/types.js +2 -0
  144. package/build/executor/changes/types.js.map +1 -0
  145. package/build/executor/changes.d.ts +3 -30
  146. package/build/executor/changes.d.ts.map +1 -1
  147. package/build/executor/changes.js +42 -51
  148. package/build/executor/changes.js.map +1 -1
  149. package/build/executor/code/types.d.ts +17 -0
  150. package/build/executor/code/types.d.ts.map +1 -0
  151. package/build/executor/code/types.js +2 -0
  152. package/build/executor/code/types.js.map +1 -0
  153. package/build/executor/code.d.ts +3 -14
  154. package/build/executor/code.d.ts.map +1 -1
  155. package/build/executor/code.js +50 -54
  156. package/build/executor/code.js.map +1 -1
  157. package/build/executor/consts.local.d.ts +5 -0
  158. package/build/executor/consts.local.d.ts.map +1 -0
  159. package/build/executor/consts.local.js +6 -0
  160. package/build/executor/consts.local.js.map +1 -0
  161. package/build/executor/creator/types.d.ts +33 -0
  162. package/build/executor/creator/types.d.ts.map +1 -0
  163. package/build/executor/creator/types.js +2 -0
  164. package/build/executor/creator/types.js.map +1 -0
  165. package/build/executor/creator.d.ts +11 -0
  166. package/build/executor/creator.d.ts.map +1 -0
  167. package/build/executor/creator.js +32 -0
  168. package/build/executor/creator.js.map +1 -0
  169. package/build/executor/resolve/types.d.ts +30 -0
  170. package/build/executor/resolve/types.d.ts.map +1 -0
  171. package/build/executor/resolve/types.js +2 -0
  172. package/build/executor/resolve/types.js.map +1 -0
  173. package/build/executor/resolve.d.ts +5 -34
  174. package/build/executor/resolve.d.ts.map +1 -1
  175. package/build/executor/resolve.js +169 -110
  176. package/build/executor/resolve.js.map +1 -1
  177. package/build/executor/types.d.ts +23 -0
  178. package/build/executor/types.d.ts.map +1 -0
  179. package/build/executor/types.js +2 -0
  180. package/build/executor/types.js.map +1 -0
  181. package/build/executor/validate.d.ts +6 -5
  182. package/build/executor/validate.d.ts.map +1 -1
  183. package/build/executor/validate.js +38 -27
  184. package/build/executor/validate.js.map +1 -1
  185. package/build/executor.d.ts +11 -7
  186. package/build/executor.d.ts.map +1 -1
  187. package/build/executor.js +27 -21
  188. package/build/executor.js.map +1 -1
  189. package/build/facade.d.ts +20 -2
  190. package/build/facade.d.ts.map +1 -1
  191. package/build/facade.js +144 -22
  192. package/build/facade.js.map +1 -1
  193. package/build/helper.d.ts +5 -9
  194. package/build/helper.d.ts.map +1 -1
  195. package/build/helper.js +12 -11
  196. package/build/helper.js.map +1 -1
  197. package/build/hierarchy/types.d.ts +20 -0
  198. package/build/hierarchy/types.d.ts.map +1 -0
  199. package/build/hierarchy/types.js +2 -0
  200. package/build/hierarchy/types.js.map +1 -0
  201. package/build/hierarchy.d.ts +5 -0
  202. package/build/hierarchy.d.ts.map +1 -0
  203. package/build/hierarchy.js +133 -0
  204. package/build/hierarchy.js.map +1 -0
  205. package/build/index.d.ts +1 -0
  206. package/build/index.d.ts.map +1 -1
  207. package/build/index.js +1 -0
  208. package/build/index.js.map +1 -1
  209. package/build/invariants/types.d.ts +6 -0
  210. package/build/invariants/types.d.ts.map +1 -0
  211. package/build/invariants/types.js +2 -0
  212. package/build/invariants/types.js.map +1 -0
  213. package/build/invariants.d.ts +5 -0
  214. package/build/invariants.d.ts.map +1 -0
  215. package/build/invariants.js +126 -0
  216. package/build/invariants.js.map +1 -0
  217. package/build/projection.d.ts +4 -10
  218. package/build/projection.d.ts.map +1 -1
  219. package/build/projection.js +7 -5
  220. package/build/projection.js.map +1 -1
  221. package/build/queue/consts.d.ts +5 -0
  222. package/build/queue/consts.d.ts.map +1 -0
  223. package/build/queue/consts.js +5 -0
  224. package/build/queue/consts.js.map +1 -0
  225. package/build/queue/declare.d.ts +5 -0
  226. package/build/queue/declare.d.ts.map +1 -0
  227. package/build/queue/declare.js +5 -0
  228. package/build/queue/declare.js.map +1 -0
  229. package/build/queue/index.d.ts +3 -18
  230. package/build/queue/index.d.ts.map +1 -1
  231. package/build/queue/index.js +2 -7
  232. package/build/queue/index.js.map +1 -1
  233. package/build/queue/types.d.ts +13 -0
  234. package/build/queue/types.d.ts.map +1 -0
  235. package/build/queue/types.js +2 -0
  236. package/build/queue/types.js.map +1 -0
  237. package/build/registry.d.ts +5 -22
  238. package/build/registry.d.ts.map +1 -1
  239. package/build/registry.js +16 -10
  240. package/build/registry.js.map +1 -1
  241. package/build/resources.d.ts +5 -0
  242. package/build/resources.d.ts.map +1 -0
  243. package/build/resources.js +287 -0
  244. package/build/resources.js.map +1 -0
  245. package/build/service.d.ts +3 -16
  246. package/build/service.d.ts.map +1 -1
  247. package/build/service.js +12 -0
  248. package/build/service.js.map +1 -1
  249. package/build/store/commits.d.ts.map +1 -1
  250. package/build/store/commits.js +3 -1
  251. package/build/store/commits.js.map +1 -1
  252. package/build/store/composite.d.ts +1 -0
  253. package/build/store/composite.d.ts.map +1 -1
  254. package/build/store/composite.js +7 -2
  255. package/build/store/composite.js.map +1 -1
  256. package/build/store/consts.local.d.ts +2 -0
  257. package/build/store/consts.local.d.ts.map +1 -0
  258. package/build/store/consts.local.js +2 -0
  259. package/build/store/consts.local.js.map +1 -0
  260. package/build/store/fold/types.d.ts +43 -0
  261. package/build/store/fold/types.d.ts.map +1 -0
  262. package/build/store/fold/types.js +2 -0
  263. package/build/store/fold/types.js.map +1 -0
  264. package/build/store/fold.d.ts +8 -30
  265. package/build/store/fold.d.ts.map +1 -1
  266. package/build/store/fold.js +190 -198
  267. package/build/store/fold.js.map +1 -1
  268. package/build/store/index.d.ts +1 -0
  269. package/build/store/index.d.ts.map +1 -1
  270. package/build/store/index.js.map +1 -1
  271. package/build/store/memory.d.ts +12 -0
  272. package/build/store/memory.d.ts.map +1 -1
  273. package/build/store/memory.js +0 -0
  274. package/build/store/memory.js.map +1 -1
  275. package/build/store/types.d.ts +15 -1
  276. package/build/store/types.d.ts.map +1 -1
  277. package/build/store/types.local.d.ts +5 -0
  278. package/build/store/types.local.d.ts.map +1 -0
  279. package/build/store/types.local.js +2 -0
  280. package/build/store/types.local.js.map +1 -0
  281. package/build/types.d.ts +126 -2
  282. package/build/types.d.ts.map +1 -1
  283. package/build/types.local.d.ts +7 -0
  284. package/build/types.local.d.ts.map +1 -0
  285. package/build/types.local.js +2 -0
  286. package/build/types.local.js.map +1 -0
  287. package/build/utils/access/types.d.ts +30 -0
  288. package/build/utils/access/types.d.ts.map +1 -0
  289. package/build/utils/access/types.js +2 -0
  290. package/build/utils/access/types.js.map +1 -0
  291. package/build/utils/access.d.ts +4 -0
  292. package/build/utils/access.d.ts.map +1 -0
  293. package/build/utils/access.js +28 -0
  294. package/build/utils/access.js.map +1 -0
  295. package/build/utils/guard/types.d.ts +23 -0
  296. package/build/utils/guard/types.d.ts.map +1 -0
  297. package/build/utils/guard/types.js +2 -0
  298. package/build/utils/guard/types.js.map +1 -0
  299. package/build/utils/guard.d.ts +7 -10
  300. package/build/utils/guard.d.ts.map +1 -1
  301. package/build/utils/guard.js +36 -32
  302. package/build/utils/guard.js.map +1 -1
  303. package/build/utils/handler/types.d.ts +26 -0
  304. package/build/utils/handler/types.d.ts.map +1 -0
  305. package/build/utils/handler/types.js +2 -0
  306. package/build/utils/handler/types.js.map +1 -0
  307. package/build/utils/handler.d.ts +6 -0
  308. package/build/utils/handler.d.ts.map +1 -0
  309. package/build/utils/handler.js +21 -0
  310. package/build/utils/handler.js.map +1 -0
  311. package/build/utils/index.d.ts +8 -0
  312. package/build/utils/index.d.ts.map +1 -1
  313. package/build/utils/index.js +3 -0
  314. package/build/utils/index.js.map +1 -1
  315. package/build/utils/request/types.d.ts +23 -0
  316. package/build/utils/request/types.d.ts.map +1 -0
  317. package/build/utils/request/types.js +2 -0
  318. package/build/utils/request/types.js.map +1 -0
  319. package/build/utils/request.d.ts +4 -0
  320. package/build/utils/request.d.ts.map +1 -0
  321. package/build/utils/request.js +27 -0
  322. package/build/utils/request.js.map +1 -0
  323. package/build/utils/scope.d.ts +17 -13
  324. package/build/utils/scope.d.ts.map +1 -1
  325. package/build/utils/scope.js +21 -35
  326. package/build/utils/scope.js.map +1 -1
  327. package/build/utils/types.d.ts +8 -0
  328. package/build/utils/types.d.ts.map +1 -0
  329. package/build/utils/types.js +2 -0
  330. package/build/utils/types.js.map +1 -0
  331. package/package.json +30 -22
  332. package/src/actions/apply-schema-request.ts +36 -0
  333. package/src/actions/commit.ts +7 -5
  334. package/src/actions/define-schemas.ts +34 -0
  335. package/src/actions/execute.ts +32 -34
  336. package/src/actions/execution/types.ts +34 -0
  337. package/src/actions/execution.ts +100 -0
  338. package/src/actions/get-card.ts +14 -0
  339. package/src/actions/get-specification.ts +14 -0
  340. package/src/actions/index.ts +13 -3
  341. package/src/actions/links.ts +8 -6
  342. package/src/actions/list-card-specifications.ts +15 -0
  343. package/src/actions/list-card-transitions.ts +15 -0
  344. package/src/actions/list-cards.ts +14 -0
  345. package/src/actions/list-schemas.ts +29 -0
  346. package/src/actions/list-specification-revisions.ts +15 -0
  347. package/src/actions/resources.ts +79 -0
  348. package/src/actions/summarize-cards.ts +15 -0
  349. package/src/actions/transitions.ts +6 -4
  350. package/src/actions/types.ts +5 -0
  351. package/src/actions/watch.ts +11 -7
  352. package/src/conformance/assert/types.ts +16 -0
  353. package/src/conformance/assert.ts +67 -0
  354. package/src/conformance/cases.ts +631 -0
  355. package/src/conformance/clock.ts +11 -0
  356. package/src/conformance/consts.ts +165 -0
  357. package/src/conformance/errors.ts +7 -0
  358. package/src/conformance/fixtures/types.ts +11 -0
  359. package/src/conformance/fixtures.ts +46 -0
  360. package/src/conformance/index.ts +14 -0
  361. package/src/conformance/resources.ts +227 -0
  362. package/src/conformance/types.ts +20 -0
  363. package/src/consts.ts +6 -0
  364. package/src/definitions.ts +264 -0
  365. package/src/executor/changes/types.ts +38 -0
  366. package/src/executor/changes.ts +49 -68
  367. package/src/executor/code/types.ts +23 -0
  368. package/src/executor/code.ts +62 -69
  369. package/src/executor/consts.local.ts +8 -0
  370. package/src/executor/creator/types.ts +33 -0
  371. package/src/executor/creator.ts +43 -0
  372. package/src/executor/resolve/types.ts +30 -0
  373. package/src/executor/resolve.ts +181 -139
  374. package/src/executor/types.ts +23 -0
  375. package/src/executor/validate.ts +40 -38
  376. package/src/executor.ts +33 -27
  377. package/src/facade.ts +132 -29
  378. package/src/helper.ts +12 -11
  379. package/src/hierarchy/types.ts +20 -0
  380. package/src/hierarchy.ts +113 -0
  381. package/src/index.ts +1 -0
  382. package/src/invariants/types.ts +6 -0
  383. package/src/invariants.ts +106 -0
  384. package/src/projection.ts +12 -18
  385. package/src/queue/consts.ts +5 -0
  386. package/src/queue/declare.ts +7 -0
  387. package/src/queue/index.ts +3 -25
  388. package/src/queue/types.ts +14 -0
  389. package/src/registry.ts +18 -36
  390. package/src/resources.ts +256 -0
  391. package/src/service.ts +16 -20
  392. package/src/store/commits.ts +5 -4
  393. package/src/store/composite.ts +7 -2
  394. package/src/store/consts.local.ts +1 -0
  395. package/src/store/fold/types.ts +44 -0
  396. package/src/store/fold.ts +214 -212
  397. package/src/store/index.ts +1 -0
  398. package/src/store/memory.ts +0 -0
  399. package/src/store/types.local.ts +3 -0
  400. package/src/store/types.ts +16 -1
  401. package/src/types.local.ts +7 -0
  402. package/src/types.ts +139 -6
  403. package/src/utils/access/types.ts +30 -0
  404. package/src/utils/access.ts +33 -0
  405. package/src/utils/guard/types.ts +21 -0
  406. package/src/utils/guard.ts +44 -31
  407. package/src/utils/handler/types.ts +28 -0
  408. package/src/utils/handler.ts +35 -0
  409. package/src/utils/index.ts +9 -0
  410. package/src/utils/request/types.ts +23 -0
  411. package/src/utils/request.ts +40 -0
  412. package/src/utils/scope.ts +39 -42
  413. package/src/utils/types.ts +8 -0
  414. package/tests/access.spec.ts +160 -0
  415. package/tests/app.spec.ts +112 -0
  416. package/tests/conformance.spec.ts +47 -0
  417. package/tests/create-example.spec.ts +202 -0
  418. package/tests/creator.spec.ts +257 -0
  419. package/tests/entrypoints.spec.ts +5 -0
  420. package/tests/memory-store.spec.ts +52 -0
  421. package/tests/narrowing.spec.ts +94 -0
  422. package/tests/plugins.spec.ts +4 -3
  423. package/tests/writes.spec.ts +140 -0
  424. package/build/actions/cards.d.ts +0 -16
  425. package/build/actions/cards.d.ts.map +0 -1
  426. package/build/actions/cards.js +0 -32
  427. package/build/actions/cards.js.map +0 -1
  428. package/build/actions/schemas.d.ts +0 -6
  429. package/build/actions/schemas.d.ts.map +0 -1
  430. package/build/actions/schemas.js +0 -9
  431. package/build/actions/schemas.js.map +0 -1
  432. package/build/actions/specs.d.ts +0 -10
  433. package/build/actions/specs.d.ts.map +0 -1
  434. package/build/actions/specs.js +0 -14
  435. package/build/actions/specs.js.map +0 -1
  436. package/src/actions/cards.ts +0 -52
  437. package/src/actions/schemas.ts +0 -15
  438. package/src/actions/specs.ts +0 -23
@@ -0,0 +1,43 @@
1
+ import { PlanningError, TransitionAction, type PlanningScope, type TransitionExecution } from '@owlmeans/planning'
2
+ import { CREATOR } from './consts.local.js'
3
+ import type { CreatorHelper } from './creator/types.js'
4
+
5
+ const named = (value?: string | null): value is string => value != null && value !== ''
6
+
7
+ export const createCreatorHelper = (): CreatorHelper => {
8
+ const creatorOf = (scope: PlanningScope): string | undefined =>
9
+ [scope.profileId, scope.userId, scope.actor?.profileId, scope.actor?.userId].find(named)
10
+
11
+ const withCreator = (exec: TransitionExecution, scope: PlanningScope): TransitionExecution => {
12
+ if (exec.action !== TransitionAction.Create || exec.card == null || typeof exec.card !== 'object') {
13
+ return exec
14
+ }
15
+ const creator = named(exec.card.createdBy) ? exec.card.createdBy : creatorOf(scope)
16
+ return { ...exec, card: { ...exec.card,
17
+ ...(creator != null ? { createdBy: creator } : {}),
18
+ ...(exec.card.reporter == null && scope.assigneeId != null ? { reporter: scope.assigneeId } : {}),
19
+ ...(exec.card.assignee == null && scope.defaultAssigneeId != null ? { assignee: scope.defaultAssigneeId } : {}),
20
+ } }
21
+ }
22
+
23
+ const assertCreatorFixed = (exec: TransitionExecution): void => {
24
+ if ((exec.changes as Record<string, unknown> | undefined)?.[CREATOR] !== undefined
25
+ || (exec.unset ?? []).some(path => path.split('.')[0] === CREATOR)) {
26
+ throw new PlanningError(`immutable:${CREATOR}`)
27
+ }
28
+ }
29
+
30
+ return { creatorOf, withCreator, assertCreatorFixed }
31
+ }
32
+
33
+ export const creatorHelper = createCreatorHelper()
34
+
35
+ /** @deprecated compat:factory-refactor — use `creatorHelper.creatorOf(…)` */
36
+ export const creatorOf = (scope: PlanningScope): string | undefined => creatorHelper.creatorOf(scope)
37
+
38
+ /** @deprecated compat:factory-refactor — use `creatorHelper.withCreator(…)` */
39
+ export const withCreator = (exec: TransitionExecution, scope: PlanningScope): TransitionExecution =>
40
+ creatorHelper.withCreator(exec, scope)
41
+
42
+ /** @deprecated compat:factory-refactor — use `creatorHelper.assertCreatorFixed(…)` */
43
+ export const assertCreatorFixed = (exec: TransitionExecution): void => creatorHelper.assertCreatorFixed(exec)
@@ -0,0 +1,30 @@
1
+ import type { PlanningFacade, PlanningSchemaRegistry, TransitionExecution, Workcard, WorkcardKind } from '@owlmeans/planning'
2
+ import type { Resolved } from '../types.js'
3
+
4
+ /** Steps 1 and 4 of the write path, over one service runtime. */
5
+ export interface ResolveUtils {
6
+ /**
7
+ * Step 1: a deep copy with trimmed text, `parents ∋ parent` on a draft, and no duplicate `unset`
8
+ * paths. The caller's object is never touched.
9
+ */
10
+ normalizeExecution: (input: TransitionExecution) => TransitionExecution
11
+ /**
12
+ * The parent type's rule for a child of `kind`/`type`: a project lists the `cardTypes` (and
13
+ * `projectTypes`) it admits, and one declaring `scopedCardTypes` also admits a card type its layer
14
+ * (`view`, the parent project's resolved schemas) defines as data. Project types are code's alone.
15
+ *
16
+ * @throws {CardTypeNotAllowed}
17
+ */
18
+ assertChildAllowed: (parent: Workcard, kind: WorkcardKind, type: string, view?: PlanningSchemaRegistry) => void
19
+ /** The layer a parent project admits data-defined children through — `undefined` without a schema port. */
20
+ childViewOf: (entityId: string, parent: Workcard) => Promise<PlanningSchemaRegistry | undefined>
21
+ /**
22
+ * Step 4: the card, its type and flow, its parent and — for a specification — its slot.
23
+ *
24
+ * @throws {WorkcardNotFound | PlanningScopeMismatch | UnknownWorkcardType | UnknownStatusFlow}
25
+ * @throws {ParentNotFound | CardTypeNotAllowed | PlanningError}
26
+ */
27
+ resolveExecution: (facade: PlanningFacade, exec: TransitionExecution) => Promise<Resolved>
28
+ /** The project a transition is filed under: a project's own id, else its parent's project. */
29
+ projectFor: (resolved: Resolved, exec: TransitionExecution, cardId: string) => string | undefined
30
+ }
@@ -1,33 +1,25 @@
1
- import {
2
- CardTypeNotAllowed, isProject, normalizeParents, ParentNotFound, PlanningError, PlanningScopeMismatch,
3
- primaryFlowOf, projectOf, slotOf, TransitionAction, UnknownStatusFlow, WorkcardKind, WorkcardNotFound,
4
- } from '@owlmeans/planning'
5
- import type {
6
- AnyTypeSchema, PlanningFacade, PlanningStore, ProjectTypeSchema, Specification, SpecificationSlot,
7
- StatusFlowSchema, TransitionExecution, Workcard, WorkcardDraft,
8
- } from '@owlmeans/planning'
9
- import type { PlanningRuntime } from '../service.js'
10
-
11
- /** Everything the steps after resolution read about an execution. */
12
- export interface Resolved {
13
- create: boolean
14
- /** The existing card (not a create). */
15
- card?: Workcard
16
- type: AnyTypeSchema
17
- flowId: string
18
- flow: StatusFlowSchema
19
- /** The primary parent, when it resolves. */
20
- parent?: Workcard
21
- /** The slot a specification belongs to. */
22
- slot?: SpecificationSlot
23
- /** The store the type is written to. */
24
- store: PlanningStore
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 { makePlanningHierarchy } from '../hierarchy.js'
3
+ import { memoHelper } from '@owlmeans/context'
4
+ import type { PlanningRuntime } from '../types.js'
5
+ import { TRIMMED } from './consts.local.js'
6
+ import type { Resolved } from './types.js'
7
+ import type { ResolveUtils } from './resolve/types.js'
8
+
9
+ const isScoped = (schemas: PlanningSchemaRegistry): schemas is ScopedSchemaRegistry =>
10
+ typeof (schemas as Partial<ScopedSchemaRegistry>).originOf === 'function'
11
+
12
+ /** A type the layer resolves from a data-defined record rather than from code. */
13
+ const dataDefined = (schemas: PlanningSchemaRegistry | undefined, type: string): boolean => {
14
+ if (schemas == null || !isScoped(schemas) || !schemas.has(type)) {
15
+ return false
16
+ }
17
+ const origin = schemas.originOf(PlanningSchemaKind.Type, type)
18
+ return origin === SchemaOrigin.Entity || origin === SchemaOrigin.Project
25
19
  }
26
20
 
27
21
  const trimmed = (value: unknown): unknown => typeof value === 'string' ? value.trim() : value
28
22
 
29
- const TRIMMED = ['title', 'description', 'code'] as const
30
-
31
23
  const trim = <T extends object>(record: T | undefined): T | undefined => {
32
24
  if (record == null) {
33
25
  return record
@@ -41,134 +33,184 @@ const trim = <T extends object>(record: T | undefined): T | undefined => {
41
33
  return copy as T
42
34
  }
43
35
 
44
- /**
45
- * Step 1: a deep copy with trimmed text, `parents ∋ parent` on a draft, and no duplicate `unset`
46
- * paths. The caller's object is never touched.
47
- */
48
- export const normalizeExecution = (input: TransitionExecution): TransitionExecution => {
49
- const exec = structuredClone(input) as TransitionExecution
50
- if (exec.card != null && typeof exec.card === 'object') {
51
- const draft = trim(exec.card) as WorkcardDraft
52
- const parents = normalizeParents(draft.parent, draft.parents)
53
- exec.card = { ...draft, ...(parents.length > 0 ? { parents, parent: draft.parent ?? parents[0] } : {}) }
54
- }
55
- if (exec.changes != null) {
56
- exec.changes = trim(exec.changes)
57
- }
58
- if (exec.unset != null) {
59
- exec.unset = [...new Set(exec.unset)]
36
+ export const makeResolveUtils = (runtime: PlanningRuntime): ResolveUtils => {
37
+ const hierarchy = makePlanningHierarchy(runtime)
38
+ const normalizeExecution = (input: TransitionExecution): TransitionExecution => {
39
+ const exec = structuredClone(input) as TransitionExecution
40
+ if (exec.card != null && typeof exec.card === 'object') {
41
+ const draft = trim(exec.card) as WorkcardDraft
42
+ const parents = cardHelper.normalizeParents(draft.parent, draft.parents)
43
+ exec.card = { ...draft, ...(parents.length > 0 ? { parents, parent: draft.parent ?? parents[0] } : {}) }
44
+ }
45
+ if (exec.changes != null) {
46
+ exec.changes = trim(exec.changes)
47
+ }
48
+ if (exec.unset != null) {
49
+ exec.unset = [...new Set(exec.unset)]
50
+ }
51
+ return exec
60
52
  }
61
- return exec
62
- }
63
53
 
64
- /** The parent type's rule for a child of `kind`/`type`. @throws {CardTypeNotAllowed} */
65
- export const assertChildAllowed = (
66
- runtime: PlanningRuntime, parent: Workcard, kind: WorkcardKind, type: string
67
- ): void => {
68
- if (kind === WorkcardKind.Specification) {
69
- return
70
- }
71
- if (!isProject(parent)) {
72
- throw new CardTypeNotAllowed(`${parent.type}:${type}`)
73
- }
74
- const parentType = runtime.service().schemas.type(parent.type) as ProjectTypeSchema
75
- const allowed = kind === WorkcardKind.Project ? parentType.projectTypes ?? [] : parentType.cardTypes ?? []
76
- if (!allowed.includes(type)) {
54
+ const assertChildAllowed = (
55
+ parent: Workcard, kind: WorkcardKind, type: string, view?: PlanningSchemaRegistry
56
+ ): void => {
57
+ if (kind === WorkcardKind.Specification) {
58
+ return
59
+ }
60
+ const registry = view ?? runtime.service().schemas
61
+ const parentType = registry.type(parent.type) as ProjectTypeSchema
62
+ const childType = registry.has(type) ? registry.type(type) : undefined
63
+ if (childType?.parents?.types != null && !childType.parents.types.includes(parent.type)) throw new CardTypeNotAllowed(`${type}:parent:${parent.type}`)
64
+ if (parentType.children != null) {
65
+ if (!parentType.children.types.includes(type)) throw new CardTypeNotAllowed(`${parent.type}:${type}`)
66
+ return
67
+ }
68
+ if (!cardHelper.isProject(parent)) throw new CardTypeNotAllowed(`${parent.type}:${type}`)
69
+ const allowed = kind === WorkcardKind.Project ? parentType.projectTypes ?? [] : parentType.cardTypes ?? []
70
+ if (allowed.includes(type)) {
71
+ return
72
+ }
73
+ if (kind === WorkcardKind.Card && parentType.scopedCardTypes === true && dataDefined(view, type)) {
74
+ return
75
+ }
77
76
  throw new CardTypeNotAllowed(`${parent.type}:${type}`)
78
77
  }
79
- }
80
78
 
81
- /**
82
- * Step 4: the card, its type and flow, its parent and — for a specification — its slot.
83
- *
84
- * @throws {WorkcardNotFound | PlanningScopeMismatch | UnknownWorkcardType | UnknownStatusFlow}
85
- * @throws {ParentNotFound | CardTypeNotAllowed | PlanningError}
86
- */
87
- export const resolveExecution = async (
88
- runtime: PlanningRuntime, facade: PlanningFacade, exec: TransitionExecution
89
- ): Promise<Resolved> => {
90
- const service = runtime.service()
91
- const schemas = service.schemas
92
- const entityId = facade.scope.entityId
93
- const create = exec.action === TransitionAction.Create
94
-
95
- let card: Workcard | undefined
96
- let type: AnyTypeSchema
97
- let parent: Workcard | undefined
98
-
99
- if (create) {
100
- if (exec.card == null || typeof exec.card !== 'object') {
101
- throw new PlanningError('malformed:create-without-draft')
102
- }
103
- const draft = exec.card
104
- type = schemas.type(draft.type)
105
- if (type.kind !== draft.kind) {
106
- throw new PlanningError(`malformed:kind:${draft.type}:${draft.kind}`)
107
- }
108
- // Normalized: the primary parent is first. It must accept the child; so must every project
109
- // among the secondary parents.
110
- for (const [index, id] of normalizeParents(draft.parent, draft.parents).entries()) {
111
- const found = await facade.cards.load(id)
79
+ const childViewOf = async (
80
+ entityId: string, parent: Workcard
81
+ ): Promise<PlanningSchemaRegistry | undefined> =>
82
+ runtime.schemaStore() == null ? undefined : await runtime.schemasFor(entityId, await hierarchy.project(parent))
83
+
84
+ const resolveExecution = async (
85
+ facade: PlanningFacade, exec: TransitionExecution
86
+ ): Promise<Resolved> => {
87
+ const service = runtime.service()
88
+ const entityId = facade.scope.entityId
89
+ const create = exec.action === TransitionAction.Create
90
+ // Without a schema port every step reads the code registry itself, in today's order; with one,
91
+ // the card's project is found first and the step reads that project's resolved layer.
92
+ const layered = runtime.schemaStore() != null
93
+ let schemas: PlanningSchemaRegistry = service.schemas
94
+
95
+ let card: Workcard | undefined
96
+ let type: AnyTypeSchema
97
+ let parent: Workcard | undefined
98
+ let project: string | undefined
99
+
100
+ if (create) {
101
+ if (exec.card == null || typeof exec.card !== 'object') {
102
+ throw new PlanningError('malformed:create-without-draft')
103
+ }
104
+ const draft = exec.card
105
+ const parents = cardHelper.normalizeParents(draft.parent, draft.parents)
106
+ const loaded = new Map<string, Workcard>()
107
+ if (layered) {
108
+ // Loaded through the facade, so a parent this scope cannot see is absent here too.
109
+ const primary = parents[0] != null ? await facade.cards.load(parents[0]) : null
110
+ if (parents[0] != null && primary == null) {
111
+ throw new ParentNotFound(parents[0])
112
+ }
113
+ if (primary != null) {
114
+ loaded.set(parents[0], primary)
115
+ }
116
+ const project = draft.kind === WorkcardKind.Project || primary == null
117
+ ? undefined
118
+ : await hierarchy.project({ kind: draft.kind, entityId, parent: parents[0], parents } as unknown as Workcard)
119
+ schemas = await runtime.schemasFor(entityId, project)
120
+ }
121
+ type = schemas.type(draft.type)
122
+ if (type.kind !== draft.kind) {
123
+ throw new PlanningError(`malformed:kind:${draft.type}:${draft.kind}`)
124
+ }
125
+ if (layered && isScoped(schemas) && schemas.isRetired(PlanningSchemaKind.Type, draft.type)) {
126
+ // Existing cards keep resolving a retired type; nothing new is made of it.
127
+ throw new UnknownWorkcardType(`retired:${draft.type}`)
128
+ }
129
+ // Normalized: the primary parent is first. It must accept the child; so must every project
130
+ // among the secondary parents.
131
+ for (const [index, id] of parents.entries()) {
132
+ const found = loaded.get(id) ?? await facade.cards.load(id)
133
+ if (found == null) {
134
+ throw new ParentNotFound(id)
135
+ }
136
+ if (index === 0) {
137
+ parent = found
138
+ }
139
+ if (found != null) {
140
+ const view = !layered ? undefined : index === 0 && cardHelper.isProject(found) ? schemas : await childViewOf(entityId, found)
141
+ assertChildAllowed(found, draft.kind, draft.type, view)
142
+ }
143
+ }
144
+ if (draft.kind === WorkcardKind.Specification && parent == null) {
145
+ throw new PlanningError('malformed:specification-without-parent')
146
+ }
147
+ } else {
148
+ if (typeof exec.card !== 'string' || exec.card === '') {
149
+ throw new PlanningError(`malformed:${exec.action}-without-card`)
150
+ }
151
+ const found = await runtime.reader().cards.get(exec.card, entityId)
112
152
  if (found == null) {
113
- throw new ParentNotFound(id)
153
+ throw new WorkcardNotFound(exec.card)
114
154
  }
115
- if (index === 0) {
116
- parent = found
155
+ if (found.entityId !== entityId) {
156
+ throw new PlanningScopeMismatch(exec.card)
117
157
  }
118
- if (index === 0 || isProject(found)) {
119
- assertChildAllowed(runtime, found, draft.kind, draft.type)
158
+ if (facade.scope.projects != null && await facade.cards.load(exec.card) == null) {
159
+ // Outside the scope's projects: absent, exactly like another entity's card.
160
+ throw new WorkcardNotFound(exec.card)
161
+ }
162
+ card = found
163
+ if (layered) {
164
+ parent = card.parent != null ? await facade.cards.load(card.parent) ?? undefined : undefined
165
+ schemas = await runtime.schemasFor(entityId, await hierarchy.project(card))
166
+ type = schemas.type(card.type)
167
+ } else {
168
+ type = schemas.type(card.type)
169
+ parent = card.parent != null ? await facade.cards.load(card.parent) ?? undefined : undefined
120
170
  }
121
171
  }
122
- if (draft.kind === WorkcardKind.Specification && parent == null) {
123
- throw new PlanningError('malformed:specification-without-parent')
124
- }
125
- } else {
126
- if (typeof exec.card !== 'string' || exec.card === '') {
127
- throw new PlanningError(`malformed:${exec.action}-without-card`)
128
- }
129
- const found = await runtime.reader().cards.get(exec.card, entityId)
130
- if (found == null) {
131
- throw new WorkcardNotFound(exec.card)
172
+
173
+ project = card != null ? await hierarchy.project(card)
174
+ : (exec.card as WorkcardDraft).kind === WorkcardKind.Project ? undefined
175
+ : await hierarchy.project({ ...(exec.card as WorkcardDraft), entityId } as Workcard)
176
+ const flowId = exec.flow ?? statusHelper.primaryFlowOf(type)
177
+ if (!type.flows.includes(flowId)) {
178
+ throw new UnknownStatusFlow(`${type.type}:${flowId}`)
132
179
  }
133
- if (found.entityId !== entityId) {
134
- throw new PlanningScopeMismatch(exec.card)
180
+
181
+ const kind = create ? (exec.card as WorkcardDraft).kind : card!.kind
182
+ const category = create ? (exec.card as WorkcardDraft).category : (card as Specification | undefined)?.category
183
+ const slot = kind === WorkcardKind.Specification && parent != null && category != null
184
+ ? specificationHelper.slotOf(schemas.type(parent.type), category)
185
+ : undefined
186
+
187
+ return {
188
+ create,
189
+ project,
190
+ ...(card != null ? { card } : {}),
191
+ type,
192
+ flowId,
193
+ flow: schemas.flow(flowId),
194
+ ...(parent != null ? { parent } : {}),
195
+ ...(slot != null ? { slot } : {}),
196
+ store: service.store(type.type),
197
+ schemas,
135
198
  }
136
- card = found
137
- type = schemas.type(card.type)
138
- parent = card.parent != null ? await facade.cards.load(card.parent) ?? undefined : undefined
139
199
  }
140
200
 
141
- const flowId = exec.flow ?? primaryFlowOf(type)
142
- if (!type.flows.includes(flowId)) {
143
- throw new UnknownStatusFlow(`${type.type}:${flowId}`)
201
+ const projectFor = (resolved: Resolved, exec: TransitionExecution, cardId: string): string | undefined => {
202
+ if (resolved.card != null) {
203
+ return cardHelper.isProject(resolved.card) ? resolved.card.id : resolved.project
204
+ }
205
+ const draft = exec.card as WorkcardDraft
206
+ if (draft.kind === WorkcardKind.Project) {
207
+ return cardId
208
+ }
209
+ return resolved.project
144
210
  }
145
211
 
146
- const kind = create ? (exec.card as WorkcardDraft).kind : card!.kind
147
- const category = create ? (exec.card as WorkcardDraft).category : (card as Specification | undefined)?.category
148
- const slot = kind === WorkcardKind.Specification && parent != null && category != null
149
- ? slotOf(schemas.type(parent.type), category)
150
- : undefined
151
-
152
- return {
153
- create,
154
- ...(card != null ? { card } : {}),
155
- type,
156
- flowId,
157
- flow: schemas.flow(flowId),
158
- ...(parent != null ? { parent } : {}),
159
- ...(slot != null ? { slot } : {}),
160
- store: service.store(type.type),
161
- }
212
+ return { normalizeExecution, assertChildAllowed, childViewOf, resolveExecution, projectFor }
162
213
  }
163
214
 
164
- /** The project a transition is filed under: a project's own id, else its parent's project. */
165
- export const projectFor = (resolved: Resolved, exec: TransitionExecution, cardId: string): string | undefined => {
166
- if (resolved.card != null) {
167
- return isProject(resolved.card) ? resolved.card.id : projectOf(resolved.card, resolved.parent)
168
- }
169
- const draft = exec.card as WorkcardDraft
170
- if (draft.kind === WorkcardKind.Project) {
171
- return cardId
172
- }
173
- return projectOf({ kind: draft.kind, parent: draft.parent, parents: draft.parents ?? [] } as unknown as Workcard, resolved.parent)
174
- }
215
+ /** The resolution steps of one service runtime — one per runtime. */
216
+ export const resolveUtilsOf = memoHelper.oncePer(makeResolveUtils)
@@ -0,0 +1,23 @@
1
+ import type { AnyTypeSchema, PlanningSchemaRegistry, PlanningStore, SpecificationSlot, StatusFlowSchema, Workcard } from '@owlmeans/planning'
2
+
3
+ /** Everything the steps after resolution read about an execution. */
4
+ export interface Resolved {
5
+ project?: string
6
+ create: boolean
7
+ /** The existing card (not a create). */
8
+ card?: Workcard
9
+ type: AnyTypeSchema
10
+ flowId: string
11
+ flow: StatusFlowSchema
12
+ /** The primary parent, when it resolves. */
13
+ parent?: Workcard
14
+ /** The slot a specification belongs to. */
15
+ slot?: SpecificationSlot
16
+ /** The store the type is written to. */
17
+ store: PlanningStore
18
+ /**
19
+ * The registry every later step reads: the service's code registry itself, or — where the store
20
+ * holds data-defined schemas — the resolved layer of the card's project.
21
+ */
22
+ schemas: PlanningSchemaRegistry
23
+ }
@@ -1,17 +1,9 @@
1
- import {
2
- applyUnset, assertMutable, BODY_MAX, CODE_MAX, DESCRIPTION_MAX, IllegalTransition,
3
- LabelNotAllowed, MAX_LABELS, MAX_PARENTS, mergeFields, normalizeParents, ParentNotFound, PlanningError,
4
- RelationshipRefused, ruleOf, SpecificationRevisionConflict, SpecificationSlotUnknown, slotOf, TITLE_MAX,
5
- TransitionAction, validateFields, validateSpecificationBody, WorkcardKind,
6
- } from '@owlmeans/planning'
7
- import type {
8
- PlanningFacade, RelationshipDraft, Specification, TransitionExecution, WorkcardDraft,
9
- } from '@owlmeans/planning'
10
- import type { PlanningRuntime } from '../service.js'
11
- import { assertChildAllowed } from './resolve.js'
12
- import type { Resolved } from './resolve.js'
13
-
14
- const ACTIONS = new Set<string>(Object.values(TransitionAction))
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, PlanningResourceKind, type PlanningFacade, type RelationshipDraft, type Specification, type TransitionExecution, type WorkcardDraft } from '@owlmeans/planning'
2
+ import type { PlanningRuntime } from '../types.js'
3
+ import { creatorHelper } from './creator.js'
4
+ import type { Resolved } from './types.js'
5
+ import { ACTIONS } from './consts.local.js'
6
+ import { resolveUtilsOf } from './resolve.js'
15
7
 
16
8
  const malformed = (what: string): PlanningError => new PlanningError(`malformed:${what}`)
17
9
 
@@ -58,10 +50,10 @@ const assertShape = (exec: TransitionExecution, resolved: Resolved): void => {
58
50
  assertText('code', changes.code ?? undefined, CODE_MAX, false)
59
51
  }
60
52
 
61
- const assertFlow = (exec: TransitionExecution, resolved: Resolved, runtime: PlanningRuntime): void => {
53
+ const assertFlow = (exec: TransitionExecution, resolved: Resolved): void => {
62
54
  if (resolved.create) {
63
55
  const status = (exec.card as WorkcardDraft).status
64
- const primary = runtime.service().schemas.primaryFlow(resolved.type.type)
56
+ const primary = resolved.schemas.primaryFlow(resolved.type.type)
65
57
  if (status != null && !primary.statuses.some(definition => definition.key === status)) {
66
58
  throw new IllegalTransition(`${primary.id}:create:${status}`)
67
59
  }
@@ -72,24 +64,24 @@ const assertFlow = (exec: TransitionExecution, resolved: Resolved, runtime: Plan
72
64
  }
73
65
  const card = resolved.card!
74
66
  const from = card.flows[resolved.flowId] ?? card.status
75
- if (ruleOf(resolved.flow, exec.transition!, from) == null) {
67
+ if (statusHelper.ruleOf(resolved.flow, exec.transition!, from) == null) {
76
68
  throw new IllegalTransition(`${resolved.flowId}:${exec.transition}:${from}`)
77
69
  }
78
70
  }
79
71
 
80
- const assertFields = (exec: TransitionExecution, resolved: Resolved, runtime: PlanningRuntime): void => {
81
- const schemas = runtime.service().schemas
72
+ const assertFields = (exec: TransitionExecution, resolved: Resolved): void => {
73
+ const schemas = resolved.schemas
82
74
  if (resolved.create) {
83
75
  const draft = exec.card as WorkcardDraft
84
- 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))
85
77
  return
86
78
  }
87
79
  const clears = (exec.unset ?? []).filter(path => path.startsWith('fields.'))
88
80
  if (exec.changes?.fields == null && clears.length === 0) {
89
81
  return
90
82
  }
91
- const merged = applyUnset({ fields: mergeFields(resolved.card!.fields, exec.changes?.fields) }, clears)
92
- 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)
93
85
  }
94
86
 
95
87
  const assertLabels = (exec: TransitionExecution, resolved: Resolved): void => {
@@ -117,7 +109,8 @@ const assertParentMove = async (
117
109
  return
118
110
  }
119
111
  const changes = exec.changes ?? {}
120
- 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))]
121
114
  .filter(id => id !== resolved.card!.parent && !resolved.card!.parents.includes(id))
122
115
  for (const id of moved) {
123
116
  if (id === resolved.card!.id) {
@@ -127,18 +120,20 @@ const assertParentMove = async (
127
120
  if (found == null) {
128
121
  throw new ParentNotFound(id)
129
122
  }
130
- assertChildAllowed(runtime, found, resolved.card!.kind, resolved.card!.type)
123
+ resolve.assertChildAllowed(
124
+ found, resolved.card!.kind, resolved.card!.type, await resolve.childViewOf(facade.scope.entityId, found)
125
+ )
131
126
  }
132
127
  }
133
128
 
134
129
  const assertSpecification = async (
135
- exec: TransitionExecution, resolved: Resolved, runtime: PlanningRuntime, facade: PlanningFacade
130
+ exec: TransitionExecution, resolved: Resolved, facade: PlanningFacade
136
131
  ): Promise<void> => {
137
132
  const kind = resolved.create ? (exec.card as WorkcardDraft).kind : resolved.card!.kind
138
133
  if (kind !== WorkcardKind.Specification) {
139
134
  return
140
135
  }
141
- const schemas = runtime.service().schemas
136
+ const schemas = resolved.schemas
142
137
 
143
138
  if (resolved.create) {
144
139
  const draft = exec.card as WorkcardDraft
@@ -146,7 +141,7 @@ const assertSpecification = async (
146
141
  if (draft.category == null || draft.category === '') {
147
142
  throw malformed('specification-without-category')
148
143
  }
149
- const slot = slotOf(schemas.type(parent.type), draft.category)
144
+ const slot = specificationHelper.slotOf(schemas.type(parent.type), draft.category)
150
145
  if (slot == null) {
151
146
  throw new SpecificationSlotUnknown(`${parent.type}:${draft.category}`)
152
147
  }
@@ -154,7 +149,7 @@ const assertSpecification = async (
154
149
  throw malformed(`format:${draft.category}:${draft.format}`)
155
150
  }
156
151
  assertText('body', draft.body, BODY_MAX, false)
157
- validateSpecificationBody(slot, draft.body)
152
+ validateHelper.validateSpecificationBody(slot, draft.body)
158
153
  if (slot.multiple !== true && await facade.specifications.current(parent.id!, draft.category) != null) {
159
154
  throw new SpecificationRevisionConflict(`${parent.id}:${draft.category}`)
160
155
  }
@@ -175,7 +170,7 @@ const assertSpecification = async (
175
170
  }
176
171
  if (changes.body != null) {
177
172
  assertText('body', changes.body, BODY_MAX, false)
178
- validateSpecificationBody(slot, changes.body)
173
+ validateHelper.validateSpecificationBody(slot, changes.body)
179
174
  }
180
175
  }
181
176
 
@@ -207,15 +202,20 @@ const assertRelationships = async (
207
202
  }
208
203
  continue
209
204
  }
210
- const target = await facade.cards.load(draft.to)
205
+ const kind = rule.toKind ?? PlanningResourceKind.Workcard
206
+ if (draft.toKind != null && draft.toKind !== kind) throw new RelationshipRefused(`to-kind:${draft.type}`)
207
+ if (draft.fromKind != null && draft.fromKind !== PlanningResourceKind.Workcard) throw new RelationshipRefused(`from-kind:${draft.type}`)
208
+ draft.toKind = kind
209
+ const target = kind === PlanningResourceKind.Assignee ? await facade.assignees.load(draft.to)
210
+ : kind === PlanningResourceKind.Team ? await facade.teams.load(draft.to) : await facade.cards.load(draft.to)
211
211
  if (target == null) {
212
212
  throw new RelationshipRefused(`to:${draft.to}`)
213
213
  }
214
214
  if (rule.from != null && !rule.from.includes(resolved.type.type)) {
215
215
  throw new RelationshipRefused(`from-type:${draft.type}:${resolved.type.type}`)
216
216
  }
217
- if (rule.to != null && !rule.to.includes(target.type)) {
218
- throw new RelationshipRefused(`to-type:${draft.type}:${target.type}`)
217
+ if (rule.to != null && !rule.to.includes('type' in target ? target.type : '')) {
218
+ throw new RelationshipRefused(`to-type:${draft.type}:${'type' in target ? target.type : kind}`)
219
219
  }
220
220
  if (rule.single === true) {
221
221
  const count = (counted.get(draft.type) ?? 0) + 1
@@ -232,8 +232,9 @@ const assertRelationships = async (
232
232
  }
233
233
 
234
234
  /**
235
- * Step 6: shape, flow rule, immutables, `fields`, labels, parent moves, the specification slot and
236
- * relationships — in that order. Nothing is written by a refusal here.
235
+ * Step 6: shape, flow rule, immutables (`createdBy` among them, `creatorHelper.assertCreatorFixed`),
236
+ * `fields`, labels, parent moves, the specification slot and relationships — in that order.
237
+ * Nothing is written by a refusal here.
237
238
  *
238
239
  * @throws {PlanningError} `malformed:*` / `immutable:*`
239
240
  * @throws {IllegalTransition | FieldsInvalid | LabelNotAllowed | ParentNotFound | CardTypeNotAllowed}
@@ -243,11 +244,12 @@ export const validateExecution = async (
243
244
  runtime: PlanningRuntime, facade: PlanningFacade, exec: TransitionExecution, resolved: Resolved
244
245
  ): Promise<void> => {
245
246
  assertShape(exec, resolved)
246
- assertFlow(exec, resolved, runtime)
247
- assertMutable(exec, resolved.type)
248
- assertFields(exec, resolved, runtime)
247
+ assertFlow(exec, resolved)
248
+ changesHelper.assertMutable(exec, resolved.type)
249
+ creatorHelper.assertCreatorFixed(exec)
250
+ assertFields(exec, resolved)
249
251
  assertLabels(exec, resolved)
250
252
  await assertParentMove(exec, resolved, runtime, facade)
251
- await assertSpecification(exec, resolved, runtime, facade)
253
+ await assertSpecification(exec, resolved, facade)
252
254
  await assertRelationships(exec, resolved, facade)
253
255
  }