@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
package/src/executor.ts CHANGED
@@ -1,17 +1,18 @@
1
- import { uuid } from '@owlmeans/basic-ids'
1
+
2
2
  import type { BasicConfig, BasicContext } from '@owlmeans/context'
3
- import { PlanningError, PlanningUnsupported, WorkcardConflict } from '@owlmeans/planning'
4
- import type {
5
- ExecuteOptions, PlanningExecContext, PlanningFacade, PlanningPlugin, PlanningStore, Transition,
6
- TransitionExecution, TransitionReceipt,
7
- } from '@owlmeans/planning'
8
- import { changeSetOf, transitionOf } from './executor/changes.js'
9
- import { assignCode } from './executor/code.js'
3
+ import { applyHelper, PlanningError, PlanningUnsupported, WorkcardConflict, type ExecuteOptions, type PlanningExecContext, type PlanningFacade, type PlanningPlugin, type PlanningStore, type Transition, type TransitionExecution, type TransitionReceipt } from '@owlmeans/planning'
4
+ import { changesUtils } from './executor/changes.js'
5
+ import { codeUtils } from './executor/code.js'
6
+ import { creatorHelper } from './executor/creator.js'
10
7
  import { makeReceipt } from './executor/receipt.js'
11
- import { normalizeExecution, projectFor, resolveExecution } from './executor/resolve.js'
12
- import type { Resolved } from './executor/resolve.js'
8
+ import type { Resolved } from './executor/types.js'
13
9
  import { validateExecution } from './executor/validate.js'
14
- import type { PlanningRuntime } from './service.js'
10
+ import type { PlanningRuntime } from './types.js'
11
+ import { resolveUtilsOf } from './executor/resolve.js'
12
+ import { idHelper } from '@owlmeans/basic-ids'
13
+
14
+ export { assertCreatorFixed, createCreatorHelper, creatorHelper, creatorOf, withCreator } from './executor/creator.js'
15
+ export type * from './executor/creator/types.js'
15
16
 
16
17
  const isoNow = (): string => new Date().toISOString()
17
18
 
@@ -25,7 +26,7 @@ const execContextOf = (
25
26
  ) => (plugin: PlanningPlugin): PlanningExecContext => ({
26
27
  context: runtime.context() as BasicContext<BasicConfig>,
27
28
  scope: facade.scope,
28
- schemas: runtime.service().schemas,
29
+ schemas: resolved.schemas,
29
30
  store: resolved.store,
30
31
  facade,
31
32
  plugin,
@@ -64,11 +65,13 @@ const currentReceipt = async (
64
65
  /**
65
66
  * THE write path. Nothing is appended before step 11, so every refusal leaves the log untouched.
66
67
  *
67
- * 1 normalize → 2 scope → 3 idempotency (a known `key` answers its first receipt, before any
68
- * validation) → 4 resolve → 5 the plugins' `before` chain (re-resolved once when it moved the type
69
- * or the parent) → 6 validate → 7 allocate the card id → 8 code → 9 changes (an update changing
70
- * nothing answers the current receipt and appends nothing) → 10 seq, a CAS against the head →
71
- * 11 append → 12 request the projection → 13 receipt → 14 `wait`.
68
+ * 1 normalize (a create's `createdBy` defaults to the scope's subject — `creatorHelper.withCreator`) →
69
+ * 2 scope → 3 idempotency (a known `key` answers its first receipt, before any validation) →
70
+ * 4 resolve → 5 the plugins' `before` chain (re-resolved once when it moved the type or the parent)
71
+ * → 6 validate (a `createdBy` in `changes`/`unset` is refused — `creatorHelper.assertCreatorFixed`) →
72
+ * 7 allocate the card id → 8 code → 9 changes (an update changing nothing answers
73
+ * the current receipt and appends nothing) → 10 seq, a CAS against the head → 11 append →
74
+ * 12 request the projection → 13 receipt → 14 `wait`.
72
75
  *
73
76
  * `after` hooks are NOT run here — the process that folds runs them, once per commit.
74
77
  */
@@ -76,10 +79,10 @@ export const executeTransition = async (
76
79
  runtime: PlanningRuntime, facade: PlanningFacade, input: TransitionExecution, opts?: ExecuteOptions
77
80
  ): Promise<TransitionReceipt> => {
78
81
  const scope = facade.scope
79
- const service = runtime.service()
80
82
  const at = (runtime.options.now ?? isoNow)()
83
+ const resolve = resolveUtilsOf(runtime)
81
84
 
82
- let exec = normalizeExecution(input)
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 resolveExecution(runtime, facade, exec)
98
+ let resolved = await resolve.resolveExecution(facade, exec)
96
99
 
97
100
  const before = identityOf(exec)
98
- exec = 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 resolveExecution(runtime, facade, exec)
103
+ resolved = await resolve.resolveExecution(facade, exec)
101
104
  }
102
105
 
103
106
  await validateExecution(runtime, facade, exec, resolved)
@@ -109,24 +112,27 @@ 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, service.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
  }
121
124
 
125
+ const preview = changesUtils.transitionOf({ exec, resolved, scope, set, cardId, seq: (resolved.card?.seq ?? 0) + 1, at, project: resolve.projectFor(resolved, exec, cardId) })
126
+ await store.validateProjection?.(resolved.card ?? null, applyHelper.applyTransition(resolved.card, preview), preview)
127
+
122
128
  const seq = await transitions.nextSeq(cardId, resolved.create ? null : exec.expectSeq ?? null)
123
129
  if (resolved.create && seq !== 1) {
124
130
  throw new WorkcardConflict(`${cardId}:create:${seq}`)
125
131
  }
126
132
 
127
- const transition = transitionOf({
133
+ const transition = changesUtils.transitionOf({
128
134
  exec, resolved, scope, set, cardId, seq, at,
129
- project: projectFor(resolved, exec, cardId),
135
+ project: resolve.projectFor(resolved, exec, cardId),
130
136
  })
131
137
 
132
138
  let appended: Transition
package/src/facade.ts CHANGED
@@ -1,14 +1,27 @@
1
- import {
2
- criteriaOf, currentSpecification, isSpecification, linkWhereOf, listOptionsOf, modelOf,
3
- PlanningUnsupported, specCriteriaOf, transitionWhereOf, WorkcardKind, WorkcardNotFound,
4
- } from '@owlmeans/planning'
5
- import type {
6
- CommitSource, PlanningFacade, PlanningScope, Specification, Workcard, WorkcardModel,
7
- } from '@owlmeans/planning'
1
+ import { cardHelper, modelOf, PlanningResourceKind, PlanningUnsupported, queryHelper, specificationHelper, WorkcardKind, WorkcardNotFound, type CommitEvent, type CommitSource, type PlanningFacade, type PlanningScope, type Relationship, type Specification, type Transition, type Workcard, type WorkcardModel, type WorkcardQuery } from '@owlmeans/planning'
2
+ import { recordQueryHelper } from '@owlmeans/resource'
3
+ import type { Criteria } from '@owlmeans/resource'
4
+ import { makeResourceFacade } from './resources.js'
5
+ import { makeDefinitions } from './definitions.js'
8
6
  import { executeTransition } from './executor.js'
9
- import type { PlanningRuntime } from './service.js'
7
+ import type { PlanningRuntime } from './types.js'
8
+ import { makePlanningHierarchy } from './hierarchy.js'
10
9
  import type { CommitHub } from './store/types.js'
11
10
 
11
+ /**
12
+ * The criteria a scope narrowed to `projects` adds to every card list, count and summary: the
13
+ * projects themselves, every card whose `parents` name one of them, and the specifications whose
14
+ * parent card is one of `through` — the cards of those projects a specification is seen through,
15
+ * exactly as a single read sees it (a specification's `parents` hold only its own card).
16
+ */
17
+ export const projectCriteriaOf = (projects: readonly string[], through: readonly string[] = []): Criteria<Workcard> => ({
18
+ $or: [
19
+ { id: { $in: [...projects] } },
20
+ { parents: { $overlaps: [...projects] } },
21
+ ...(through.length > 0 ? [{ kind: WorkcardKind.Specification, parent: { $in: [...through] } }] : []),
22
+ ],
23
+ }) as Criteria<Workcard>
24
+
12
25
  /**
13
26
  * The scoped facade over the service's stores.
14
27
  *
@@ -16,13 +29,44 @@ import type { CommitHub } from './store/types.js'
16
29
  * a card of another entity is simply absent — `load` answers `null`, `get` throws
17
30
  * `WorkcardNotFound`, exactly as for an id that never existed. Reads go through the composite
18
31
  * store (the default store plus the stores plugins own); writes go through the executor.
32
+ *
33
+ * A scope naming `projects` sees those projects, the cards whose `parents` name one of them, and a
34
+ * specification whose parent card is one of those (a card's specification through its card);
35
+ * everything else reads as absent. Lists, counts and summaries are narrowed by an AND'd criteria
36
+ * that admits the same cards a single read does — the parent cards a specification is seen through
37
+ * are resolved once per call ({@link projectCriteriaOf}) — and commit events and transitions
38
+ * outside the set are dropped or refused.
39
+ *
40
+ * `definitions` is present when the default store holds data-defined schemas; a model is then
41
+ * built over the registry of the card's own project.
19
42
  */
20
43
  export const makeStoreFacade = (runtime: PlanningRuntime, scope: PlanningScope): PlanningFacade => {
21
44
  const entityId = scope.entityId
45
+ const projects = scope.projects != null ? [...new Set(scope.projects)] : undefined
22
46
  const reader = () => runtime.reader()
23
47
  const mine = <T extends { entityId: string }>(record: T | null | undefined): T | null =>
24
48
  record != null && record.entityId === entityId ? record : null
25
49
 
50
+ const hierarchy = makePlanningHierarchy(runtime)
51
+ const visible = async (card: Workcard | null): Promise<Workcard | null> =>
52
+ card != null && await hierarchy.visible(card, projects) ? card : null
53
+ const allowedIds = async (): Promise<string[]> => {
54
+ if (projects == null) return []
55
+ const result = new Set<string>()
56
+ for (const project of projects) {
57
+ const root = mine(await reader().cards.get(project, entityId))
58
+ if (root == null) continue
59
+ result.add(project)
60
+ for (const card of await hierarchy.descendants(project, entityId)) result.add(card.id!)
61
+ }
62
+ return [...result]
63
+ }
64
+ const narrow = async (where: Criteria<Workcard>): Promise<Criteria<Workcard>> => projects == null ? where
65
+ : { ...where, $and: [...(where.$and ?? []), { id: { $in: await allowedIds() } }] }
66
+ const narrowed = async (query?: WorkcardQuery | null): Promise<Criteria<Workcard>> => await narrow(queryHelper.criteriaOf(query, scope))
67
+ const projectVisible = (project?: string | null): boolean =>
68
+ projects == null || (project != null && projects.includes(project))
69
+
26
70
  const commitSource = (): CommitSource => {
27
71
  const commits = reader().commits
28
72
  if (commits == null) {
@@ -31,28 +75,37 @@ export const makeStoreFacade = (runtime: PlanningRuntime, scope: PlanningScope):
31
75
  return commits
32
76
  }
33
77
 
34
- /** The transition is this entity's — its row, or the remembered commit of a purged one. */
78
+ /** The transition is this entity's (and this scope's) — its row, or the remembered commit of a purged one. */
79
+ const transitionVisible = async (row: Pick<Transition, 'card' | 'project'>): Promise<boolean> => {
80
+ if (projects == null) return true
81
+ const card = mine(await reader().cards.get(row.card, entityId))
82
+ return card != null ? await hierarchy.visible(card, projects) : projectVisible(row.project)
83
+ }
35
84
  const assertTransition = async (transition: string): Promise<void> => {
36
85
  const row = await reader().transitions?.get(transition)
37
86
  if (row != null) {
38
- if (row.entityId !== entityId) {
87
+ if (row.entityId !== entityId || !await transitionVisible(row)) {
39
88
  throw new WorkcardNotFound(`transition:${transition}`)
40
89
  }
41
90
  return
42
91
  }
43
92
  const recalled = (reader().commits as Partial<CommitHub> | undefined)?.recall?.(transition)
44
- if (recalled == null || recalled.entityId !== entityId) {
93
+ if (recalled == null || recalled.entityId !== entityId || !projectVisible(recalled.project)) {
45
94
  throw new WorkcardNotFound(`transition:${transition}`)
46
95
  }
47
96
  }
48
97
 
98
+ /** The project a card resolves its data-defined types in. */
99
+ const projectFor = async (card: Workcard): Promise<string | undefined> => await hierarchy.project(card)
100
+
49
101
  const facade: PlanningFacade = {
50
- scope: Object.freeze({ ...scope }),
102
+ ...makeResourceFacade(runtime, () => facade),
103
+ scope: Object.freeze({ ...scope, ...(projects != null ? { projects } : {}) }),
51
104
 
52
105
  schemas: runtime.service().schemas,
53
106
 
54
107
  cards: {
55
- load: async id => mine(await reader().cards.get(id, entityId)),
108
+ load: async id => await visible(mine(await reader().cards.get(id, entityId))),
56
109
 
57
110
  get: async id => {
58
111
  const card = await facade.cards.load(id)
@@ -62,36 +115,42 @@ export const makeStoreFacade = (runtime: PlanningRuntime, scope: PlanningScope):
62
115
  return card
63
116
  },
64
117
 
65
- list: async query => await reader().cards.list(criteriaOf(query, scope), listOptionsOf(query)),
118
+ list: async query => await reader().cards.list(await narrowed(query), queryHelper.listOptionsOf(query)),
66
119
 
67
- count: async query => await reader().cards.count(criteriaOf(query, scope)),
120
+ count: async query => await reader().cards.count(await narrowed(query)),
68
121
 
69
122
  summary: async (parents, query) => parents.length === 0
70
123
  ? {}
71
- : await reader().cards.summary(parents, criteriaOf({ kind: query?.kind, type: query?.type }, scope)),
124
+ : await reader().cards.summary(parents, await narrow(queryHelper.criteriaOf({ kind: query?.kind, type: query?.type }, scope))),
72
125
  },
73
126
 
74
127
  specifications: {
75
128
  current: async (parent, category) => {
129
+ if (projects != null && await facade.cards.load(parent) == null) {
130
+ return null
131
+ }
76
132
  const specs = reader().specs
77
133
  if (specs != null) {
78
134
  return mine(await specs.current(parent, category, entityId))
79
135
  }
80
- const listed = await reader().cards.list(specCriteriaOf(parent, { category }, scope) as never, { size: 0 })
81
- return currentSpecification(listed.items, category)
136
+ const listed = await reader().cards.list(queryHelper.specCriteriaOf(parent, { category }, scope) as never, { size: 0 })
137
+ return specificationHelper.currentSpecification(listed.items, category)
82
138
  },
83
139
 
84
140
  list: async (parent, query) => {
141
+ if (projects != null && await facade.cards.load(parent) == null) {
142
+ return { items: [], total: 0 }
143
+ }
85
144
  const specs = reader().specs
86
145
  if (specs != null) {
87
146
  return await specs.list(parent, entityId, query)
88
147
  }
89
- return await reader().cards.list(specCriteriaOf(parent, query, scope) as never, listOptionsOf(query) as never) as never
148
+ return await reader().cards.list(queryHelper.specCriteriaOf(parent, query, scope) as never, queryHelper.listOptionsOf(query) as never) as never
90
149
  },
91
150
 
92
151
  get: async id => {
93
152
  const card = await facade.cards.load(id)
94
- if (card == null || !isSpecification(card)) {
153
+ if (card == null || !cardHelper.isSpecification(card)) {
95
154
  throw new WorkcardNotFound(id)
96
155
  }
97
156
  return card as Specification
@@ -110,16 +169,44 @@ export const makeStoreFacade = (runtime: PlanningRuntime, scope: PlanningScope):
110
169
  relationships: {
111
170
  list: async query => {
112
171
  const links = reader().links
113
- return links == null
114
- ? { items: [], total: 0 }
115
- : await links.list(linkWhereOf(query, scope), listOptionsOf(query))
172
+ if (links == null) {
173
+ return { items: [], total: 0 }
174
+ }
175
+ const where = queryHelper.linkWhereOf(query, scope)
176
+ // Inverse names are views over the canonical edge, never separately stored links.
177
+ const stored = await links.list(query?.type != null ? { entityId, id: where.id, project: where.project } : where, { size: 0 })
178
+ const names = query?.type == null ? undefined : Array.isArray(query.type) ? query.type : [query.type]
179
+ const presented: Relationship[] = []
180
+ for (const link of stored.items) {
181
+ if (names == null || names.includes(link.type)) presented.push(link)
182
+ if (names == null || (link.fromKind ?? PlanningResourceKind.Workcard) !== PlanningResourceKind.Workcard) continue
183
+ const source = await reader().cards.get(link.from, entityId)
184
+ if (source == null) continue
185
+ const rules = (await runtime.schemasFor(entityId, await hierarchy.project(source))).type(source.type).relationships ?? []
186
+ for (const rule of rules) if (rule.name === link.type && rule.inverse != null && names.includes(rule.inverse)) {
187
+ presented.push({ ...link, type: rule.inverse, from: link.to, to: link.from,
188
+ fromKind: link.toKind ?? PlanningResourceKind.Workcard, toKind: link.fromKind ?? PlanningResourceKind.Workcard })
189
+ }
190
+ }
191
+ const items: Relationship[] = []
192
+ for (const link of presented) {
193
+ if (projects == null) { items.push(link); continue }
194
+ const endpoints: string[] = []
195
+ if ((link.fromKind ?? PlanningResourceKind.Workcard) === PlanningResourceKind.Workcard) endpoints.push(link.from)
196
+ if ((link.toKind ?? PlanningResourceKind.Workcard) === PlanningResourceKind.Workcard) endpoints.push(link.to)
197
+ if (endpoints.length === 0) continue
198
+ let admitted = true
199
+ for (const id of endpoints) if (await facade.cards.load(id) == null) { admitted = false; break }
200
+ if (admitted) items.push(link)
201
+ }
202
+ return recordQueryHelper.applyQuery(items, where as Criteria<Relationship>, queryHelper.listOptionsOf(query))
116
203
  },
117
204
  },
118
205
 
119
206
  transitions: {
120
207
  get: async id => {
121
208
  const transition = mine(await reader().transitions?.get(id))
122
- if (transition == null) {
209
+ if (transition == null || !await transitionVisible(transition)) {
123
210
  throw new WorkcardNotFound(`transition:${id}`)
124
211
  }
125
212
  return transition
@@ -127,9 +214,13 @@ export const makeStoreFacade = (runtime: PlanningRuntime, scope: PlanningScope):
127
214
 
128
215
  list: async query => {
129
216
  const transitions = reader().transitions
130
- return transitions == null
131
- ? { items: [], total: 0 }
132
- : await transitions.list(transitionWhereOf(query, scope), listOptionsOf(query))
217
+ if (transitions == null) {
218
+ return { items: [], total: 0 }
219
+ }
220
+ const listed = await transitions.list(queryHelper.transitionWhereOf(query, scope), { size: 0 })
221
+ const items: Transition[] = []
222
+ for (const transition of listed.items) if (await transitionVisible(transition)) items.push(transition)
223
+ return recordQueryHelper.applyQuery(items, undefined, queryHelper.listOptionsOf(query))
133
224
  },
134
225
  },
135
226
 
@@ -139,7 +230,10 @@ export const makeStoreFacade = (runtime: PlanningRuntime, scope: PlanningScope):
139
230
  return await commitSource().status(transition)
140
231
  },
141
232
 
142
- subscribe: (listener, filter) => commitSource().subscribe(listener, { ...filter, entityId }),
233
+ subscribe: (listener, filter) => commitSource().subscribe(projects == null
234
+ ? listener
235
+ : async (event: CommitEvent) => await transitionVisible(event) ? await listener(event) : undefined,
236
+ { ...filter, entityId }),
143
237
 
144
238
  wait: async (transition, opts) => {
145
239
  await assertTransition(transition)
@@ -154,9 +248,18 @@ export const makeStoreFacade = (runtime: PlanningRuntime, scope: PlanningScope):
154
248
  if (record.entityId !== entityId) {
155
249
  throw new WorkcardNotFound(record.id ?? WorkcardKind.Card)
156
250
  }
157
- return modelOf<T>(record, facade)
251
+ if (runtime.schemaStore() == null) {
252
+ return modelOf<T>(record, facade)
253
+ }
254
+ // A data-defined type answers from the registry of the card's own project.
255
+ const schemas = await runtime.schemasFor(entityId, await projectFor(record))
256
+ return modelOf<T>(record, { ...facade, schemas })
158
257
  },
159
258
  }
160
259
 
260
+ if (runtime.schemaStore() != null) {
261
+ facade.definitions = makeDefinitions(runtime, facade)
262
+ }
263
+
161
264
  return facade
162
265
  }
package/src/helper.ts CHANGED
@@ -1,23 +1,29 @@
1
+ import { servePlanningResources } from './actions/resources.js'
1
2
  import type { BasicConfig, BasicContext } from '@owlmeans/context'
2
3
  import type { AbstractRequest } from '@owlmeans/entrypoint'
3
4
  import type { PlanningFacade, PlanningProtocols, PlanningScope } from '@owlmeans/planning'
4
5
  import { bind } from '@owlmeans/server-entrypoint'
5
6
  import {
6
- executePlanning, getCard, getCommit, getSpecification, getTransition, listCardSpecifications,
7
+ defineSchemas, executePlanning, getCard, getCommit, getSpecification, getTransition, listCardSpecifications,
7
8
  listCardTransitions, listCards, listLinks, listSchemas, listSpecificationRevisions, summarizeCards,
8
9
  watchCommits,
9
10
  } from './actions/index.js'
10
11
  import type { PlanningHandlerOptions } from './types.js'
11
- import { planningServiceOf, scopeOf } from './utils/index.js'
12
+ import { planningHandlerOf } from './utils/handler.js'
12
13
 
13
14
  /**
14
15
  * Bind this package's handlers to a tree declared by `makePlanningProtocols` — one binding per
15
- * protocol, the base included. Pass the tree itself, never a flattened list; an application that
16
- * wants its own handler for one leaf binds that protocol afterwards.
16
+ * protocol, the base included (and `schema.define` when the tree declares it). Pass the tree
17
+ * itself, never a flattened list; an application that wants its own handler for one leaf binds
18
+ * that protocol afterwards.
17
19
  */
18
20
  export const servePlanningEntrypoints = (protocols: PlanningProtocols, opts?: PlanningHandlerOptions) => [
21
+ ...servePlanningResources(protocols, opts),
19
22
  bind(protocols.base),
20
23
  bind(protocols.schema.list, listSchemas(protocols.schema.list, opts)),
24
+ ...(protocols.schema.define != null
25
+ ? [bind(protocols.schema.define, defineSchemas(protocols.schema.define, opts))]
26
+ : []),
21
27
  bind(protocols.card.list, listCards(protocols.card.list, opts)),
22
28
  bind(protocols.card.summary, summarizeCards(protocols.card.summary, opts)),
23
29
  bind(protocols.card.get, getCard(protocols.card.get, opts)),
@@ -32,13 +38,8 @@ export const servePlanningEntrypoints = (protocols: PlanningProtocols, opts?: Pl
32
38
  bind(protocols.commit.events, watchCommits(protocols.commit.events, opts)),
33
39
  ]
34
40
 
35
- /**
36
- * The facade a hand-written handler works through: the request's entity and subject, plus `extra`
37
- * (a `channel`) — never an entity from the request body.
38
- *
39
- * @throws {AuthorizationError} when the request carries no organization
40
- */
41
+ /** @deprecated compat:factory-refactor — use `planningHandlerOf(ctx).planningFor(…)` */
41
42
  export const planningFor = (
42
43
  ctx: BasicContext<BasicConfig>, req: AbstractRequest, extra?: Partial<PlanningScope>,
43
44
  opts?: Pick<PlanningHandlerOptions, 'service'>
44
- ): PlanningFacade => planningServiceOf(ctx, opts).for(scopeOf(req, extra))
45
+ ): PlanningFacade => planningHandlerOf(ctx).planningFor(req, extra, opts)
@@ -0,0 +1,20 @@
1
+ import type { Workcard } from '@owlmeans/planning'
2
+
3
+ export interface PlanningHierarchy {
4
+ ancestors: (card: Pick<Workcard, 'entityId' | 'parent' | 'parents'>) => Promise<Workcard[]>
5
+ project: (card: Pick<Workcard, 'entityId' | 'kind' | 'id' | 'parent'>) => Promise<string | undefined>
6
+ projects: (card: Workcard) => Promise<string[]>
7
+ visible: (card: Workcard, projects?: readonly string[]) => Promise<boolean>
8
+ descendants: (id: string, entityId: string) => Promise<Workcard[]>
9
+ /**
10
+ * A card's own documents: the specifications whose primary parent is the card, and theirs in
11
+ * turn. They never block the card's delete — its fold removes them with it.
12
+ */
13
+ documents: (id: string, entityId: string) => Promise<Workcard[]>
14
+ /**
15
+ * The card's own shape against its schemas — or, `deleting`, whether it may go: a project always
16
+ * (its delete purges what is under it); any other card only when everything under it is one of
17
+ * its own `documents` (`hierarchy:parent-in-use` otherwise).
18
+ */
19
+ validate: (card: Workcard, deleting?: boolean) => Promise<void>
20
+ }
@@ -0,0 +1,113 @@
1
+ import {
2
+ CardTypeNotAllowed, FieldsInvalid, ParentNotFound, PlanningError, PlanningSchemaKind, SchemaOrigin,
3
+ WorkcardKind, type Workcard, type PlanningSchemaRegistry, type ProjectTypeSchema, type ScopedSchemaRegistry,
4
+ } from '@owlmeans/planning'
5
+ import type { PlanningRuntime } from './types.js'
6
+ import type { PlanningHierarchy } from './hierarchy/types.js'
7
+
8
+ /** One recursive resolver for schema inheritance, project authorization and hierarchy constraints. */
9
+ export const makePlanningHierarchy = (runtime: PlanningRuntime): PlanningHierarchy => {
10
+ const cards = () => runtime.reader().cards
11
+ const ancestors: PlanningHierarchy['ancestors'] = async card => {
12
+ const visited = new Set<string>()
13
+ const result: Workcard[] = []
14
+ const visit = async (id: string, path: Set<string>): Promise<void> => {
15
+ if (path.has(id)) throw new PlanningError(`hierarchy:cycle:${id}`)
16
+ if (visited.has(id)) return
17
+ const parent = await cards().get(id, card.entityId)
18
+ if (parent == null || parent.entityId !== card.entityId) throw new ParentNotFound(id)
19
+ const next = new Set([...path, id])
20
+ for (const reference of parent.parents) await visit(reference, next)
21
+ visited.add(id)
22
+ result.push(parent)
23
+ }
24
+ for (const id of card.parents) await visit(id, new Set())
25
+ return result
26
+ }
27
+ const project: PlanningHierarchy['project'] = async card => {
28
+ if (card.kind === WorkcardKind.Project) return card.id
29
+ const visited = new Set<string>()
30
+ let parent = card.parent
31
+ while (parent != null) {
32
+ if (visited.has(parent)) throw new PlanningError(`hierarchy:cycle:${parent}`)
33
+ visited.add(parent)
34
+ const found = await cards().get(parent, card.entityId)
35
+ if (found == null) throw new ParentNotFound(parent)
36
+ if (found.kind === WorkcardKind.Project) return found.id
37
+ parent = found.parent
38
+ }
39
+ return undefined
40
+ }
41
+ const descendants: PlanningHierarchy['descendants'] = async (id, entityId) => {
42
+ const found = new Map<string, Workcard>()
43
+ let frontier = [id]
44
+ while (frontier.length > 0) {
45
+ const result = await cards().list({ entityId, parents: { $overlaps: frontier }, kind: [WorkcardKind.Card, WorkcardKind.Project, WorkcardKind.Specification] } as never, { size: 0 })
46
+ frontier = []
47
+ for (const card of result.items) if (card.id !== id && !found.has(card.id!)) { found.set(card.id!, card); frontier.push(card.id!) }
48
+ }
49
+ return [...found.values()]
50
+ }
51
+ /** The specifications among `below` whose primary parent is `id` or one already owned. */
52
+ const ownedOf = (id: string, below: Workcard[]): Workcard[] => {
53
+ const owners = new Set([id])
54
+ const owned: Workcard[] = []
55
+ let grown = true
56
+ while (grown) {
57
+ grown = false
58
+ for (const card of below) {
59
+ if (card.kind === WorkcardKind.Specification && card.parent != null && owners.has(card.parent) && !owners.has(card.id!)) {
60
+ owners.add(card.id!)
61
+ owned.push(card)
62
+ grown = true
63
+ }
64
+ }
65
+ }
66
+ return owned
67
+ }
68
+ const assertPair = (parent: Workcard, card: Workcard, schemas: PlanningSchemaRegistry): void => {
69
+ if (card.kind === WorkcardKind.Specification) return
70
+ const childType = schemas.type(card.type)
71
+ if (childType.parents?.types != null && !childType.parents.types.includes(parent.type)) throw new CardTypeNotAllowed(`${card.type}:parent:${parent.type}`)
72
+ const parentType = schemas.type(parent.type)
73
+ if (parentType.children != null) {
74
+ if (!parentType.children.types.includes(card.type)) throw new CardTypeNotAllowed(`${parent.type}:child:${card.type}`)
75
+ return
76
+ }
77
+ if (parent.kind !== WorkcardKind.Project) throw new CardTypeNotAllowed(`${parent.type}:child:${card.type}`)
78
+ const container = parentType as ProjectTypeSchema
79
+ const allowed = card.kind === WorkcardKind.Project ? container.projectTypes ?? [] : container.cardTypes
80
+ const origin = (schemas as Partial<ScopedSchemaRegistry>).originOf?.(PlanningSchemaKind.Type, card.type)
81
+ if (!allowed.includes(card.type) && !(card.kind === WorkcardKind.Card && container.scopedCardTypes && (origin === SchemaOrigin.Project || origin === SchemaOrigin.Entity))) throw new CardTypeNotAllowed(`${parent.type}:${card.type}`)
82
+ }
83
+ const hierarchy: PlanningHierarchy = {
84
+ ancestors,
85
+ project,
86
+ descendants,
87
+ documents: async (id, entityId) => ownedOf(id, await descendants(id, entityId)),
88
+ projects: async card => [...new Set([...(card.kind === WorkcardKind.Project ? [card.id!] : []), ...(await ancestors(card)).filter(parent => parent.kind === WorkcardKind.Project).map(parent => parent.id!)])],
89
+ visible: async (card, projects) => projects == null || (await hierarchy.projects(card)).some(id => projects.includes(id)),
90
+ validate: async (card, deleting = false) => {
91
+ if (deleting) {
92
+ if (card.kind === WorkcardKind.Project) return
93
+ // The card's own documents go with it; anything else under it keeps it alive.
94
+ const below = await descendants(card.id!, card.entityId)
95
+ if (below.length > ownedOf(card.id!, below).length) throw new PlanningError(`hierarchy:parent-in-use:${card.id}`)
96
+ return
97
+ }
98
+ const schemas = await runtime.schemasFor(card.entityId, await project(card))
99
+ const type = schemas.type(card.type)
100
+ if (type.parents?.required && card.parent == null) throw new ParentNotFound(`required:${card.type}`)
101
+ if (card.parent != null && !card.parents.includes(card.parent)) throw new FieldsInvalid('hierarchy:primary-membership')
102
+ const parents = await ancestors(card)
103
+ if (parents.some(parent => parent.id === card.id)) throw new PlanningError(`hierarchy:cycle:${card.id}`)
104
+ for (const id of card.parents) {
105
+ const parent = parents.find(parent => parent.id === id)!
106
+ assertPair(parent, card, await runtime.schemasFor(card.entityId, await project(parent)))
107
+ }
108
+ if (card.parent != null) card.parentType = parents.find(parent => parent.id === card.parent)!.type
109
+ else delete card.parentType
110
+ },
111
+ }
112
+ return hierarchy
113
+ }
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@ export * from './consts.js'
5
5
  export * from './service.js'
6
6
  export * from './registry.js'
7
7
  export * from './facade.js'
8
+ export * from './definitions.js'
8
9
  export * from './executor.js'
9
10
  export * from './helper.js'
10
11
  export * from './projection.js'
@@ -0,0 +1,6 @@
1
+ import type { Workcard, Transition } from '@owlmeans/planning'
2
+
3
+ export interface PlanningInvariants {
4
+ validate: (before: Workcard | null, after: Workcard | null, transition: Transition) => Promise<void>
5
+ references: (after: Workcard, transition: Transition) => Promise<void>
6
+ }