@owlmeans/server-planning 0.1.18-rc.21 → 0.1.18-rc.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (314) hide show
  1. package/README.md +24 -17
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/server-planning/SKILL.md +28 -28
  4. package/build/actions/apply-schema-request.d.ts +8 -0
  5. package/build/actions/apply-schema-request.d.ts.map +1 -0
  6. package/build/actions/apply-schema-request.js +31 -0
  7. package/build/actions/apply-schema-request.js.map +1 -0
  8. package/build/actions/commit.d.ts +3 -3
  9. package/build/actions/commit.d.ts.map +1 -1
  10. package/build/actions/commit.js +5 -4
  11. package/build/actions/commit.js.map +1 -1
  12. package/build/actions/define-schemas.d.ts +13 -0
  13. package/build/actions/define-schemas.d.ts.map +1 -0
  14. package/build/actions/define-schemas.js +28 -0
  15. package/build/actions/define-schemas.js.map +1 -0
  16. package/build/actions/execute.d.ts +8 -28
  17. package/build/actions/execute.d.ts.map +1 -1
  18. package/build/actions/execute.js +17 -101
  19. package/build/actions/execute.js.map +1 -1
  20. package/build/actions/execution/types.d.ts +34 -0
  21. package/build/actions/execution/types.d.ts.map +1 -0
  22. package/build/actions/execution/types.js +2 -0
  23. package/build/actions/execution/types.js.map +1 -0
  24. package/build/actions/execution.d.ts +4 -0
  25. package/build/actions/execution.d.ts.map +1 -0
  26. package/build/actions/execution.js +79 -0
  27. package/build/actions/execution.js.map +1 -0
  28. package/build/actions/get-card.d.ts +6 -0
  29. package/build/actions/get-card.d.ts.map +1 -0
  30. package/build/actions/get-card.js +9 -0
  31. package/build/actions/get-card.js.map +1 -0
  32. package/build/actions/get-specification.d.ts +6 -0
  33. package/build/actions/get-specification.d.ts.map +1 -0
  34. package/build/actions/get-specification.js +9 -0
  35. package/build/actions/get-specification.js.map +1 -0
  36. package/build/actions/index.d.ts +12 -3
  37. package/build/actions/index.d.ts.map +1 -1
  38. package/build/actions/index.js +11 -3
  39. package/build/actions/index.js.map +1 -1
  40. package/build/actions/links.d.ts +3 -3
  41. package/build/actions/links.d.ts.map +1 -1
  42. package/build/actions/links.js +6 -5
  43. package/build/actions/links.js.map +1 -1
  44. package/build/actions/list-card-specifications.d.ts +6 -0
  45. package/build/actions/list-card-specifications.d.ts.map +1 -0
  46. package/build/actions/list-card-specifications.js +11 -0
  47. package/build/actions/list-card-specifications.js.map +1 -0
  48. package/build/actions/list-card-transitions.d.ts +6 -0
  49. package/build/actions/list-card-transitions.d.ts.map +1 -0
  50. package/build/actions/list-card-transitions.js +11 -0
  51. package/build/actions/list-card-transitions.js.map +1 -0
  52. package/build/actions/list-cards.d.ts +6 -0
  53. package/build/actions/list-cards.d.ts.map +1 -0
  54. package/build/actions/list-cards.js +10 -0
  55. package/build/actions/list-cards.js.map +1 -0
  56. package/build/actions/list-schemas.d.ts +13 -0
  57. package/build/actions/list-schemas.d.ts.map +1 -0
  58. package/build/actions/list-schemas.js +20 -0
  59. package/build/actions/list-schemas.js.map +1 -0
  60. package/build/actions/{specs.d.ts → list-specification-revisions.d.ts} +1 -3
  61. package/build/actions/list-specification-revisions.d.ts.map +1 -0
  62. package/build/actions/list-specification-revisions.js +10 -0
  63. package/build/actions/list-specification-revisions.js.map +1 -0
  64. package/build/actions/summarize-cards.d.ts +6 -0
  65. package/build/actions/summarize-cards.d.ts.map +1 -0
  66. package/build/actions/summarize-cards.js +11 -0
  67. package/build/actions/summarize-cards.js.map +1 -0
  68. package/build/actions/transitions.d.ts +3 -3
  69. package/build/actions/transitions.d.ts.map +1 -1
  70. package/build/actions/transitions.js +4 -3
  71. package/build/actions/transitions.js.map +1 -1
  72. package/build/actions/types.d.ts +5 -3
  73. package/build/actions/types.d.ts.map +1 -1
  74. package/build/actions/watch.d.ts +2 -2
  75. package/build/actions/watch.d.ts.map +1 -1
  76. package/build/actions/watch.js +2 -2
  77. package/build/actions/watch.js.map +1 -1
  78. package/build/conformance/assert/types.d.ts +19 -0
  79. package/build/conformance/assert/types.d.ts.map +1 -0
  80. package/build/conformance/assert/types.js +2 -0
  81. package/build/conformance/assert/types.js.map +1 -0
  82. package/build/conformance/assert.d.ts +8 -13
  83. package/build/conformance/assert.d.ts.map +1 -1
  84. package/build/conformance/assert.js +41 -43
  85. package/build/conformance/assert.js.map +1 -1
  86. package/build/conformance/cases.d.ts.map +1 -1
  87. package/build/conformance/cases.js +86 -67
  88. package/build/conformance/cases.js.map +1 -1
  89. package/build/conformance/clock.d.ts +6 -0
  90. package/build/conformance/clock.d.ts.map +1 -0
  91. package/build/conformance/clock.js +12 -0
  92. package/build/conformance/clock.js.map +1 -0
  93. package/build/conformance/consts.d.ts +3 -1
  94. package/build/conformance/consts.d.ts.map +1 -1
  95. package/build/conformance/consts.js +84 -1
  96. package/build/conformance/consts.js.map +1 -1
  97. package/build/conformance/errors.d.ts +5 -0
  98. package/build/conformance/errors.d.ts.map +1 -0
  99. package/build/conformance/errors.js +8 -0
  100. package/build/conformance/errors.js.map +1 -0
  101. package/build/conformance/fixtures/types.d.ts +13 -0
  102. package/build/conformance/fixtures/types.d.ts.map +1 -0
  103. package/build/conformance/fixtures/types.js +2 -0
  104. package/build/conformance/fixtures/types.js.map +1 -0
  105. package/build/conformance/fixtures.d.ts +8 -8
  106. package/build/conformance/fixtures.d.ts.map +1 -1
  107. package/build/conformance/fixtures.js +24 -107
  108. package/build/conformance/fixtures.js.map +1 -1
  109. package/build/conformance/index.d.ts +3 -0
  110. package/build/conformance/index.d.ts.map +1 -1
  111. package/build/conformance/index.js +1 -0
  112. package/build/conformance/index.js.map +1 -1
  113. package/build/definitions.d.ts.map +1 -1
  114. package/build/definitions.js +9 -9
  115. package/build/definitions.js.map +1 -1
  116. package/build/executor/changes/types.d.ts +36 -0
  117. package/build/executor/changes/types.d.ts.map +1 -0
  118. package/build/executor/changes/types.js +2 -0
  119. package/build/executor/changes/types.js.map +1 -0
  120. package/build/executor/changes.d.ts +3 -30
  121. package/build/executor/changes.d.ts.map +1 -1
  122. package/build/executor/changes.js +42 -51
  123. package/build/executor/changes.js.map +1 -1
  124. package/build/executor/code/types.d.ts +17 -0
  125. package/build/executor/code/types.d.ts.map +1 -0
  126. package/build/executor/code/types.js +2 -0
  127. package/build/executor/code/types.js.map +1 -0
  128. package/build/executor/code.d.ts +3 -14
  129. package/build/executor/code.d.ts.map +1 -1
  130. package/build/executor/code.js +50 -54
  131. package/build/executor/code.js.map +1 -1
  132. package/build/executor/creator/types.d.ts +33 -0
  133. package/build/executor/creator/types.d.ts.map +1 -0
  134. package/build/executor/creator/types.js +2 -0
  135. package/build/executor/creator/types.js.map +1 -0
  136. package/build/executor/creator.d.ts +6 -25
  137. package/build/executor/creator.d.ts.map +1 -1
  138. package/build/executor/creator.js +26 -41
  139. package/build/executor/creator.js.map +1 -1
  140. package/build/executor/resolve/types.d.ts +30 -0
  141. package/build/executor/resolve/types.d.ts.map +1 -0
  142. package/build/executor/resolve/types.js +2 -0
  143. package/build/executor/resolve/types.js.map +1 -0
  144. package/build/executor/resolve.d.ts +4 -41
  145. package/build/executor/resolve.d.ts.map +1 -1
  146. package/build/executor/resolve.js +142 -157
  147. package/build/executor/resolve.js.map +1 -1
  148. package/build/executor/types.d.ts +0 -2
  149. package/build/executor/types.d.ts.map +1 -1
  150. package/build/executor/validate.d.ts +1 -1
  151. package/build/executor/validate.d.ts.map +1 -1
  152. package/build/executor/validate.js +16 -15
  153. package/build/executor/validate.js.map +1 -1
  154. package/build/executor.d.ts +4 -3
  155. package/build/executor.d.ts.map +1 -1
  156. package/build/executor.js +18 -17
  157. package/build/executor.js.map +1 -1
  158. package/build/facade.d.ts.map +1 -1
  159. package/build/facade.js +16 -16
  160. package/build/facade.js.map +1 -1
  161. package/build/helper.d.ts +1 -6
  162. package/build/helper.d.ts.map +1 -1
  163. package/build/helper.js +3 -8
  164. package/build/helper.js.map +1 -1
  165. package/build/projection.d.ts +2 -0
  166. package/build/projection.d.ts.map +1 -1
  167. package/build/projection.js +6 -4
  168. package/build/projection.js.map +1 -1
  169. package/build/queue/declare.d.ts +5 -0
  170. package/build/queue/declare.d.ts.map +1 -0
  171. package/build/queue/declare.js +5 -0
  172. package/build/queue/declare.js.map +1 -0
  173. package/build/queue/index.d.ts +1 -4
  174. package/build/queue/index.d.ts.map +1 -1
  175. package/build/queue/index.js +1 -4
  176. package/build/queue/index.js.map +1 -1
  177. package/build/store/composite.d.ts.map +1 -1
  178. package/build/store/composite.js +2 -2
  179. package/build/store/composite.js.map +1 -1
  180. package/build/store/fold/types.d.ts +43 -0
  181. package/build/store/fold/types.d.ts.map +1 -0
  182. package/build/store/fold/types.js +2 -0
  183. package/build/store/fold/types.js.map +1 -0
  184. package/build/store/fold.d.ts +7 -30
  185. package/build/store/fold.d.ts.map +1 -1
  186. package/build/store/fold.js +185 -203
  187. package/build/store/fold.js.map +1 -1
  188. package/build/store/index.d.ts +1 -0
  189. package/build/store/index.d.ts.map +1 -1
  190. package/build/store/index.js.map +1 -1
  191. package/build/store/memory.d.ts.map +1 -1
  192. package/build/store/memory.js +0 -0
  193. package/build/store/memory.js.map +1 -1
  194. package/build/store/types.local.d.ts +3 -1
  195. package/build/store/types.local.d.ts.map +1 -1
  196. package/build/types.d.ts +2 -1
  197. package/build/types.d.ts.map +1 -1
  198. package/build/utils/access/types.d.ts +30 -0
  199. package/build/utils/access/types.d.ts.map +1 -0
  200. package/build/utils/access/types.js +2 -0
  201. package/build/utils/access/types.js.map +1 -0
  202. package/build/utils/access.d.ts +4 -0
  203. package/build/utils/access.d.ts.map +1 -0
  204. package/build/utils/access.js +28 -0
  205. package/build/utils/access.js.map +1 -0
  206. package/build/utils/guard/types.d.ts +21 -0
  207. package/build/utils/guard/types.d.ts.map +1 -0
  208. package/build/utils/guard/types.js +2 -0
  209. package/build/utils/guard/types.js.map +1 -0
  210. package/build/utils/guard.d.ts +7 -10
  211. package/build/utils/guard.d.ts.map +1 -1
  212. package/build/utils/guard.js +33 -31
  213. package/build/utils/guard.js.map +1 -1
  214. package/build/utils/handler/types.d.ts +26 -0
  215. package/build/utils/handler/types.d.ts.map +1 -0
  216. package/build/utils/handler/types.js +2 -0
  217. package/build/utils/handler/types.js.map +1 -0
  218. package/build/utils/handler.d.ts +6 -0
  219. package/build/utils/handler.d.ts.map +1 -0
  220. package/build/utils/handler.js +21 -0
  221. package/build/utils/handler.js.map +1 -0
  222. package/build/utils/index.d.ts +8 -1
  223. package/build/utils/index.d.ts.map +1 -1
  224. package/build/utils/index.js +3 -0
  225. package/build/utils/index.js.map +1 -1
  226. package/build/utils/request/types.d.ts +23 -0
  227. package/build/utils/request/types.d.ts.map +1 -0
  228. package/build/utils/request/types.js +2 -0
  229. package/build/utils/request/types.js.map +1 -0
  230. package/build/utils/request.d.ts +4 -0
  231. package/build/utils/request.d.ts.map +1 -0
  232. package/build/utils/request.js +27 -0
  233. package/build/utils/request.js.map +1 -0
  234. package/build/utils/scope.d.ts +10 -43
  235. package/build/utils/scope.d.ts.map +1 -1
  236. package/build/utils/scope.js +21 -99
  237. package/build/utils/scope.js.map +1 -1
  238. package/package.json +18 -18
  239. package/src/actions/apply-schema-request.ts +35 -0
  240. package/src/actions/commit.ts +7 -5
  241. package/src/actions/define-schemas.ts +34 -0
  242. package/src/actions/execute.ts +30 -115
  243. package/src/actions/execution/types.ts +34 -0
  244. package/src/actions/execution.ts +96 -0
  245. package/src/actions/get-card.ts +14 -0
  246. package/src/actions/get-specification.ts +14 -0
  247. package/src/actions/index.ts +13 -3
  248. package/src/actions/links.ts +8 -6
  249. package/src/actions/list-card-specifications.ts +15 -0
  250. package/src/actions/list-card-transitions.ts +15 -0
  251. package/src/actions/list-cards.ts +14 -0
  252. package/src/actions/list-schemas.ts +29 -0
  253. package/src/actions/{specs.ts → list-specification-revisions.ts} +4 -11
  254. package/src/actions/summarize-cards.ts +15 -0
  255. package/src/actions/transitions.ts +6 -4
  256. package/src/actions/types.ts +4 -3
  257. package/src/actions/watch.ts +4 -3
  258. package/src/conformance/assert/types.ts +16 -0
  259. package/src/conformance/assert.ts +49 -41
  260. package/src/conformance/cases.ts +88 -67
  261. package/src/conformance/clock.ts +11 -0
  262. package/src/conformance/consts.ts +87 -1
  263. package/src/conformance/errors.ts +7 -0
  264. package/src/conformance/fixtures/types.ts +11 -0
  265. package/src/conformance/fixtures.ts +32 -107
  266. package/src/conformance/index.ts +3 -0
  267. package/src/definitions.ts +9 -9
  268. package/src/executor/changes/types.ts +38 -0
  269. package/src/executor/changes.ts +48 -63
  270. package/src/executor/code/types.ts +23 -0
  271. package/src/executor/code.ts +60 -61
  272. package/src/executor/creator/types.ts +33 -0
  273. package/src/executor/creator.ts +33 -41
  274. package/src/executor/resolve/types.ts +30 -0
  275. package/src/executor/resolve.ts +153 -167
  276. package/src/executor/types.ts +0 -3
  277. package/src/executor/validate.ts +17 -16
  278. package/src/executor.ts +20 -17
  279. package/src/facade.ts +16 -16
  280. package/src/helper.ts +3 -8
  281. package/src/projection.ts +8 -4
  282. package/src/queue/declare.ts +7 -0
  283. package/src/queue/index.ts +1 -8
  284. package/src/store/composite.ts +2 -2
  285. package/src/store/fold/types.ts +44 -0
  286. package/src/store/fold.ts +209 -212
  287. package/src/store/index.ts +1 -0
  288. package/src/store/memory.ts +0 -0
  289. package/src/store/types.local.ts +1 -1
  290. package/src/types.ts +1 -1
  291. package/src/utils/access/types.ts +30 -0
  292. package/src/utils/access.ts +33 -0
  293. package/src/utils/guard/types.ts +19 -0
  294. package/src/utils/guard.ts +41 -30
  295. package/src/utils/handler/types.ts +28 -0
  296. package/src/utils/handler.ts +35 -0
  297. package/src/utils/index.ts +9 -1
  298. package/src/utils/request/types.ts +23 -0
  299. package/src/utils/request.ts +40 -0
  300. package/src/utils/scope.ts +25 -108
  301. package/tests/creator.spec.ts +18 -18
  302. package/build/actions/cards.d.ts +0 -14
  303. package/build/actions/cards.d.ts.map +0 -1
  304. package/build/actions/cards.js +0 -32
  305. package/build/actions/cards.js.map +0 -1
  306. package/build/actions/schemas.d.ts +0 -28
  307. package/build/actions/schemas.d.ts.map +0 -1
  308. package/build/actions/schemas.js +0 -69
  309. package/build/actions/schemas.js.map +0 -1
  310. package/build/actions/specs.d.ts.map +0 -1
  311. package/build/actions/specs.js +0 -14
  312. package/build/actions/specs.js.map +0 -1
  313. package/src/actions/cards.ts +0 -48
  314. package/src/actions/schemas.ts +0 -87
@@ -0,0 +1,34 @@
1
+ import type { ExecuteOptions, ExecuteRequest, PlanningFacade, PlanningScope, TransitionExecution } from '@owlmeans/planning'
2
+ import type { PlanningAccess } from '../../types.js'
3
+
4
+ /** What a wire execution may say, how long it may hold, and what access it needs. */
5
+ export interface ExecutionHelper {
6
+ /**
7
+ * What a wire execution may say. `actor` is dropped — the scope names the writer — and a create's
8
+ * `createdBy` claim is dropped too: the executor's `creatorHelper.withCreator` stamps the
9
+ * authenticated subject, the same rule an in-process create follows. Applied here as well, so the
10
+ * returned execution already carries it. A `createdBy` in `changes` or `unset` is not dropped: the
11
+ * executor refuses it on every action (`assertCreatorFixed`), so no body moves an owner.
12
+ */
13
+ wireExecution: (body: ExecuteRequest, scope: PlanningScope) => TransitionExecution
14
+ /** `wait`/`timeout` as a server grants them: the timeout never holds a request past `maxPoll`. */
15
+ executeOptionsOf: (body: Pick<ExecuteRequest, 'wait' | 'timeout'>, maxPoll: number) => ExecuteOptions
16
+ /**
17
+ * Refuse an execution outside the access's `writes`: the card it acts on (a specification through
18
+ * its parent card), or every parent a create names, must be one `writableIn` admits. A create
19
+ * of a project is `grants.createProjects`' alone. A card or parent this scope cannot see is left to
20
+ * the executor, which answers it as absent.
21
+ *
22
+ * @throws {PlanningForbidden}
23
+ */
24
+ assertExecutionWrites: (facade: PlanningFacade, exec: TransitionExecution, access?: PlanningAccess) => Promise<void>
25
+ /**
26
+ * The access a wire execution needs. `writes` first ({@link ExecutionHelper.assertExecutionWrites});
27
+ * then the grants: `createProjects` for a project create (its primary parent, or the root),
28
+ * `deleteProjects` for the delete of a project card. A card this scope cannot see is left to the
29
+ * executor, which answers it as absent.
30
+ *
31
+ * @throws {PlanningForbidden}
32
+ */
33
+ assertExecutionGranted: (facade: PlanningFacade, exec: TransitionExecution, access?: PlanningAccess) => Promise<void>
34
+ }
@@ -0,0 +1,96 @@
1
+ import {
2
+ cardHelper, DEFAULT_COMMIT_TIMEOUT, PlanningForbidden, TransitionAction, WorkcardKind,
3
+ } from '@owlmeans/planning'
4
+ import type {
5
+ ExecuteOptions, ExecuteRequest, PlanningFacade, PlanningScope, TransitionExecution, Workcard,
6
+ } from '@owlmeans/planning'
7
+ import { creatorHelper } from '../executor/creator.js'
8
+ import type { PlanningAccess } from '../types.js'
9
+ import { makePlanningAccessModel } from '../utils/access.js'
10
+ import type { ExecutionHelper } from './execution/types.js'
11
+
12
+ export const createExecutionHelper = (): ExecutionHelper => {
13
+ const wireExecution = (body: ExecuteRequest, scope: PlanningScope): TransitionExecution => {
14
+ const { wait: _wait, timeout: _timeout, actor: _actor, ...exec } = body
15
+ if (exec.action === TransitionAction.Create && exec.card != null && typeof exec.card === 'object') {
16
+ const { createdBy: _createdBy, ...draft } = exec.card
17
+ exec.card = draft
18
+ }
19
+ return creatorHelper.withCreator(exec, scope)
20
+ }
21
+
22
+ const executeOptionsOf = (body: Pick<ExecuteRequest, 'wait' | 'timeout'>, maxPoll: number): ExecuteOptions => {
23
+ const ceiling = maxPoll * 1000
24
+ if (body.wait !== true) {
25
+ return {}
26
+ }
27
+ const asked = Number(body.timeout ?? DEFAULT_COMMIT_TIMEOUT)
28
+ return { wait: true, timeout: Math.min(Number.isFinite(asked) && asked > 0 ? asked : DEFAULT_COMMIT_TIMEOUT, ceiling) }
29
+ }
30
+
31
+ const assertExecutionWrites = async (
32
+ facade: PlanningFacade, exec: TransitionExecution, access?: PlanningAccess
33
+ ): Promise<void> => {
34
+ if (access?.writes == null) {
35
+ return
36
+ }
37
+ const model = makePlanningAccessModel(access)
38
+ const admitted = async (card: Workcard): Promise<boolean> => {
39
+ if (model.writableIn(card)) {
40
+ return true
41
+ }
42
+ if (!cardHelper.isSpecification(card) || card.parent == null) {
43
+ return false
44
+ }
45
+ const parent = await facade.cards.load(card.parent)
46
+ return parent == null || model.writableIn(parent)
47
+ }
48
+
49
+ if (exec.action === TransitionAction.Create && exec.card != null && typeof exec.card === 'object') {
50
+ if (exec.card.kind === WorkcardKind.Project) {
51
+ return
52
+ }
53
+ const parents = cardHelper.normalizeParents(exec.card.parent, exec.card.parents)
54
+ if (parents.length === 0) {
55
+ throw new PlanningForbidden('writes:root')
56
+ }
57
+ for (const id of parents) {
58
+ const parent = await facade.cards.load(id)
59
+ if (parent != null && !await admitted(parent)) {
60
+ throw new PlanningForbidden(`writes:${id}`)
61
+ }
62
+ }
63
+ return
64
+ }
65
+ if (typeof exec.card === 'string' && exec.card !== '') {
66
+ const card = await facade.cards.load(exec.card)
67
+ if (card != null && !await admitted(card)) {
68
+ throw new PlanningForbidden(`writes:${exec.card}`)
69
+ }
70
+ }
71
+ }
72
+
73
+ const assertExecutionGranted = async (
74
+ facade: PlanningFacade, exec: TransitionExecution, access?: PlanningAccess
75
+ ): Promise<void> => {
76
+ await assertExecutionWrites(facade, exec, access)
77
+ if (access?.grants == null) {
78
+ return
79
+ }
80
+ const model = makePlanningAccessModel(access)
81
+ if (exec.action === TransitionAction.Create && typeof exec.card === 'object' && exec.card?.kind === WorkcardKind.Project) {
82
+ model.assertGranted('createProjects', cardHelper.normalizeParents(exec.card.parent, exec.card.parents)[0])
83
+ return
84
+ }
85
+ if (exec.action === TransitionAction.Delete && typeof exec.card === 'string') {
86
+ const card = await facade.cards.load(exec.card)
87
+ if (card != null && cardHelper.isProject(card)) {
88
+ model.assertGranted('deleteProjects', card.id)
89
+ }
90
+ }
91
+ }
92
+
93
+ return { wireExecution, executeOptionsOf, assertExecutionWrites, assertExecutionGranted }
94
+ }
95
+
96
+ export const executionHelper = createExecutionHelper()
@@ -0,0 +1,14 @@
1
+ import { handlers } from '@owlmeans/server-api'
2
+ import type { PlanningProtocols } from '@owlmeans/planning'
3
+ import type { Context, PlanningHandlerOptions } from '../types.js'
4
+ import { guardHelper } from '../utils/guard.js'
5
+ import { planningHandlerOf } from '../utils/handler.js'
6
+ import type { RequestHandler } from './types.js'
7
+
8
+ /** @throws {WorkcardNotFound} for an absent card and for another entity's alike */
9
+ export const getCard = (protocol: PlanningProtocols['card']['get'], opts?: PlanningHandlerOptions): RequestHandler =>
10
+ handlers<Context>().request(protocol, async (req, ctx) => guardHelper.concealed(async () => {
11
+ const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
12
+
13
+ return await facade.cards.get(`${req.params.id}`)
14
+ }))
@@ -0,0 +1,14 @@
1
+ import { handlers } from '@owlmeans/server-api'
2
+ import type { PlanningProtocols } from '@owlmeans/planning'
3
+ import type { Context, PlanningHandlerOptions } from '../types.js'
4
+ import { guardHelper } from '../utils/guard.js'
5
+ import { planningHandlerOf } from '../utils/handler.js'
6
+ import type { RequestHandler } from './types.js'
7
+
8
+ /** @throws {WorkcardNotFound} */
9
+ export const getSpecification = (protocol: PlanningProtocols['spec']['get'], opts?: PlanningHandlerOptions): RequestHandler =>
10
+ handlers<Context>().request(protocol, async (req, ctx) => guardHelper.concealed(async () => {
11
+ const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
12
+
13
+ return await facade.specifications.get(`${req.params.id}`)
14
+ }))
@@ -1,8 +1,18 @@
1
- export * from './cards.js'
1
+ export type * from './execution/types.js'
2
+
3
+ export * from './apply-schema-request.js'
2
4
  export * from './commit.js'
5
+ export * from './define-schemas.js'
3
6
  export * from './execute.js'
7
+ export * from './execution.js'
8
+ export * from './get-card.js'
9
+ export * from './get-specification.js'
4
10
  export * from './links.js'
5
- export * from './schemas.js'
6
- export * from './specs.js'
11
+ export * from './list-card-specifications.js'
12
+ export * from './list-card-transitions.js'
13
+ export * from './list-cards.js'
14
+ export * from './list-schemas.js'
15
+ export * from './list-specification-revisions.js'
16
+ export * from './summarize-cards.js'
7
17
  export * from './transitions.js'
8
18
  export * from './watch.js'
@@ -1,14 +1,16 @@
1
1
  import { handlers } from '@owlmeans/server-api'
2
- import { decodeRelationshipQuery } from '@owlmeans/planning'
2
+ import { wireHelper } from '@owlmeans/planning'
3
3
  import type { PlanningProtocols } from '@owlmeans/planning'
4
4
  import type { Context, PlanningHandlerOptions } from '../types.js'
5
- import { concealed, handlerFacade } from '../utils/index.js'
5
+ import { guardHelper } from '../utils/guard.js'
6
+ import { planningHandlerOf } from '../utils/handler.js'
7
+ import type { RequestHandler } from './types.js'
6
8
 
7
9
  export const listLinks = (
8
10
  protocol: PlanningProtocols['link']['list'], opts?: PlanningHandlerOptions
9
- ): ReturnType<ReturnType<typeof handlers<Context>>['request']> =>
10
- handlers<Context>().request(protocol, async (req, ctx) => concealed(async () => {
11
- const facade = await handlerFacade(ctx, req, opts)
11
+ ): RequestHandler =>
12
+ handlers<Context>().request(protocol, async (req, ctx) => guardHelper.concealed(async () => {
13
+ const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
12
14
 
13
- return await facade.relationships.list(decodeRelationshipQuery(req.query))
15
+ return await facade.relationships.list(wireHelper.decodeRelationshipQuery(req.query))
14
16
  }))
@@ -0,0 +1,15 @@
1
+ import { handlers } from '@owlmeans/server-api'
2
+ import { wireHelper, type PlanningProtocols } from '@owlmeans/planning'
3
+ import type { Context, PlanningHandlerOptions } from '../types.js'
4
+ import { guardHelper } from '../utils/guard.js'
5
+ import { planningHandlerOf } from '../utils/handler.js'
6
+ import type { RequestHandler } from './types.js'
7
+
8
+ /** One card's specifications — the current document per category unless `all`. */
9
+ export const listCardSpecifications = (protocol: PlanningProtocols['card']['specifications'], opts?: PlanningHandlerOptions): RequestHandler =>
10
+ handlers<Context>().request(protocol, async (req, ctx) => guardHelper.concealed(async () => {
11
+ const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
12
+ const card = await facade.cards.get(`${req.params.id}`)
13
+
14
+ return await facade.specifications.list(card.id!, wireHelper.decodeSpecificationQuery(req.query))
15
+ }))
@@ -0,0 +1,15 @@
1
+ import { handlers } from '@owlmeans/server-api'
2
+ import { wireHelper, type PlanningProtocols } from '@owlmeans/planning'
3
+ import type { Context, PlanningHandlerOptions } from '../types.js'
4
+ import { guardHelper } from '../utils/guard.js'
5
+ import { planningHandlerOf } from '../utils/handler.js'
6
+ import type { RequestHandler } from './types.js'
7
+
8
+ /** One card's log. */
9
+ export const listCardTransitions = (protocol: PlanningProtocols['card']['transitions'], opts?: PlanningHandlerOptions): RequestHandler =>
10
+ handlers<Context>().request(protocol, async (req, ctx) => guardHelper.concealed(async () => {
11
+ const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
12
+ const card = await facade.cards.get(`${req.params.id}`)
13
+
14
+ return await facade.transitions.list({ ...wireHelper.decodeTransitionQuery(req.query), card: card.id })
15
+ }))
@@ -0,0 +1,14 @@
1
+ import { handlers } from '@owlmeans/server-api'
2
+ import { wireHelper, type PlanningProtocols } from '@owlmeans/planning'
3
+ import type { Context, PlanningHandlerOptions } from '../types.js'
4
+ import { guardHelper } from '../utils/guard.js'
5
+ import { planningHandlerOf } from '../utils/handler.js'
6
+ import type { RequestHandler } from './types.js'
7
+
8
+ /** Cards of the caller's entity; the query arrives in its wire shape and is decoded here. */
9
+ export const listCards = (protocol: PlanningProtocols['card']['list'], opts?: PlanningHandlerOptions): RequestHandler =>
10
+ handlers<Context>().request(protocol, async (req, ctx) => guardHelper.concealed(async () => {
11
+ const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
12
+
13
+ return await facade.cards.list(wireHelper.decodeWorkcardQuery(req.query))
14
+ }))
@@ -0,0 +1,29 @@
1
+ import { handlers } from '@owlmeans/server-api'
2
+ import type { PlanningProtocols, ScopedSchemaBundle } from '@owlmeans/planning'
3
+ import type { Context, PlanningHandlerOptions } from '../types.js'
4
+ import { guardHelper } from '../utils/guard.js'
5
+ import { planningHandlerOf } from '../utils/handler.js'
6
+ import type { RequestHandler } from './types.js'
7
+
8
+ const text = (value: unknown): string | undefined =>
9
+ value == null || value === '' ? undefined : `${Array.isArray(value) ? value[0] : value}`
10
+
11
+ /**
12
+ * The schema bundle — what a client loads to answer `can()` with no round trip. Scoped like every
13
+ * other leaf (no organization, no bundle). Where the store holds data-defined schemas it is the
14
+ * resolved layer of the organization, or of the project the `project` query names (a project this
15
+ * scope can see); otherwise the service's code bundle.
16
+ *
17
+ * @throws {WorkcardNotFound} for a project this scope cannot see
18
+ */
19
+ export const listSchemas = (
20
+ protocol: PlanningProtocols['schema']['list'], opts?: PlanningHandlerOptions
21
+ ): RequestHandler =>
22
+ handlers<Context>().request(protocol, async (req, ctx) => guardHelper.concealed(async (): Promise<ScopedSchemaBundle> => {
23
+ const { facade } = await planningHandlerOf(ctx).handlerScopeOf(req, opts)
24
+ if (facade.definitions == null) {
25
+ return facade.schemas.bundle()
26
+ }
27
+
28
+ return await facade.definitions.bundle(text((req.query as { project?: unknown } | undefined)?.project))
29
+ }))
@@ -1,21 +1,14 @@
1
1
  import { handlers } from '@owlmeans/server-api'
2
2
  import type { PlanningProtocols } from '@owlmeans/planning'
3
3
  import type { Context, PlanningHandlerOptions } from '../types.js'
4
- import { concealed, handlerFacade } from '../utils/index.js'
4
+ import { guardHelper } from '../utils/guard.js'
5
+ import { planningHandlerOf } from '../utils/handler.js'
5
6
  import type { RequestHandler } from './types.js'
6
7
 
7
- /** @throws {WorkcardNotFound} */
8
- export const getSpecification = (protocol: PlanningProtocols['spec']['get'], opts?: PlanningHandlerOptions): RequestHandler =>
9
- handlers<Context>().request(protocol, async (req, ctx) => concealed(async () => {
10
- const facade = await handlerFacade(ctx, req, opts)
11
-
12
- return await facade.specifications.get(`${req.params.id}`)
13
- }))
14
-
15
8
  /** A document's history, newest first, replayed from its log. */
16
9
  export const listSpecificationRevisions = (protocol: PlanningProtocols['spec']['revisions'], opts?: PlanningHandlerOptions): RequestHandler =>
17
- handlers<Context>().request(protocol, async (req, ctx) => concealed(async () => {
18
- const facade = await handlerFacade(ctx, req, opts)
10
+ handlers<Context>().request(protocol, async (req, ctx) => guardHelper.concealed(async () => {
11
+ const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
19
12
  const limit = req.query?.limit == null ? undefined : Number(req.query.limit)
20
13
 
21
14
  return { items: await facade.specifications.revisions(`${req.params.id}`, Number.isFinite(limit) ? limit : undefined) }
@@ -0,0 +1,15 @@
1
+ import { handlers } from '@owlmeans/server-api'
2
+ import { wireHelper, type PlanningProtocols } from '@owlmeans/planning'
3
+ import type { Context, PlanningHandlerOptions } from '../types.js'
4
+ import { guardHelper } from '../utils/guard.js'
5
+ import { planningHandlerOf } from '../utils/handler.js'
6
+ import type { RequestHandler } from './types.js'
7
+
8
+ /** Intrinsic counts of DIRECT children per parent. */
9
+ export const summarizeCards = (protocol: PlanningProtocols['card']['summary'], opts?: PlanningHandlerOptions): RequestHandler =>
10
+ handlers<Context>().request(protocol, async (req, ctx) => guardHelper.concealed(async () => {
11
+ const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
12
+ const { parents, ...query } = wireHelper.decodeSummaryQuery(req.query)
13
+
14
+ return await facade.cards.summary(parents, query)
15
+ }))
@@ -1,14 +1,16 @@
1
1
  import { handlers } from '@owlmeans/server-api'
2
2
  import type { PlanningProtocols } from '@owlmeans/planning'
3
3
  import type { Context, PlanningHandlerOptions } from '../types.js'
4
- import { concealed, handlerFacade } from '../utils/index.js'
4
+ import { guardHelper } from '../utils/guard.js'
5
+ import { planningHandlerOf } from '../utils/handler.js'
6
+ import type { RequestHandler } from './types.js'
5
7
 
6
8
  /** @throws {WorkcardNotFound} for an absent transition and for another entity's alike */
7
9
  export const getTransition = (
8
10
  protocol: PlanningProtocols['transition']['get'], opts?: PlanningHandlerOptions
9
- ): ReturnType<ReturnType<typeof handlers<Context>>['request']> =>
10
- handlers<Context>().request(protocol, async (req, ctx) => concealed(async () => {
11
- const facade = await handlerFacade(ctx, req, opts)
11
+ ): RequestHandler =>
12
+ handlers<Context>().request(protocol, async (req, ctx) => guardHelper.concealed(async () => {
13
+ const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
12
14
 
13
15
  return await facade.transitions.get(`${req.params.transition}`)
14
16
  }))
@@ -1,4 +1,5 @@
1
- import { handlers } from '@owlmeans/server-api'
2
- import type { Context } from '../types.js'
1
+ import type { EntrypointProtocolDeclaration } from '@owlmeans/entrypoint'
2
+ import type { BoundEntrypointHandler } from '@owlmeans/server-entrypoint'
3
3
 
4
- export type RequestHandler = ReturnType<ReturnType<typeof handlers<Context>>['request']>
4
+ /** A handler bound to one protocol leaf — what `handlers().request()` and `connection()` answer. */
5
+ export interface RequestHandler extends BoundEntrypointHandler<EntrypointProtocolDeclaration> {}
@@ -5,7 +5,8 @@ import { connection } from '@owlmeans/server-socket'
5
5
  import type { EventMessage } from '@owlmeans/socket'
6
6
  import { MessageType } from '@owlmeans/socket'
7
7
  import type { Context, PlanningHandlerOptions } from '../types.js'
8
- import { handlerFacade } from '../utils/index.js'
8
+ import { planningHandlerOf } from '../utils/handler.js'
9
+ import type { RequestHandler } from './types.js'
9
10
 
10
11
  const log = logger('planning:watch')
11
12
 
@@ -21,8 +22,8 @@ const text = (value: unknown): string | undefined =>
21
22
  */
22
23
  export const watchCommits = (
23
24
  protocol: PlanningProtocols['commit']['events'], opts?: PlanningHandlerOptions
24
- ): ReturnType<typeof connection> => connection<typeof protocol, Context>(protocol, async (conn, ctx, req) => {
25
- const facade = await handlerFacade(ctx, req, opts)
25
+ ): RequestHandler => connection<typeof protocol, Context>(protocol, async (conn, ctx, req) => {
26
+ const facade = await planningHandlerOf(ctx).handlerFacade(req, opts)
26
27
  const query = (req.query ?? {}) as CommitFeedQuery
27
28
  const frame = opts?.event ?? PLANNING_COMMIT_EVENT
28
29
  const filter = {
@@ -0,0 +1,16 @@
1
+ /** The expectations a conformance case states — each throws `ConformanceFailure` when it does not hold. */
2
+ export interface AssertHelper {
3
+ /** @throws {ConformanceFailure} */
4
+ check: (condition: unknown, message: string) => void
5
+ /** Structural equality over JSON values. @throws {ConformanceFailure} */
6
+ same: (actual: unknown, expected: unknown, message: string) => void
7
+ /** The same members in any order. @throws {ConformanceFailure} */
8
+ sameSet: (actual: readonly unknown[], expected: readonly unknown[], message: string) => void
9
+ /**
10
+ * The promise rejects with an error of the class (matched by instance or by its registered type
11
+ * name, so a duplicate module copy counts). Answers the error.
12
+ *
13
+ * @throws {ConformanceFailure}
14
+ */
15
+ rejects: (run: Promise<unknown> | (() => Promise<unknown>), expected: { typeName: string }, message: string) => Promise<Error>
16
+ }
@@ -1,12 +1,8 @@
1
- import { sameValue } from '@owlmeans/planning'
1
+ import { changesHelper } from '@owlmeans/planning'
2
+ import type { AssertHelper } from './assert/types.js'
3
+ import { ConformanceFailure } from './errors.js'
2
4
 
3
- /** A conformance expectation that did not hold. Plain `Error`, so any test runner reports it. */
4
- export class ConformanceFailure extends Error {
5
- constructor(message: string) {
6
- super(`planning conformance: ${message}`)
7
- this.name = 'ConformanceFailure'
8
- }
9
- }
5
+ export { ConformanceFailure } from './errors.js'
10
6
 
11
7
  const show = (value: unknown): string => {
12
8
  try {
@@ -16,44 +12,56 @@ const show = (value: unknown): string => {
16
12
  }
17
13
  }
18
14
 
19
- /** @throws {ConformanceFailure} */
20
- export const check = (condition: unknown, message: string): void => {
21
- if (condition !== true) {
22
- throw new ConformanceFailure(message)
15
+ export const createAssertHelper = (): AssertHelper => {
16
+ const check = (condition: unknown, message: string): void => {
17
+ if (condition !== true) {
18
+ throw new ConformanceFailure(message)
19
+ }
23
20
  }
24
- }
25
21
 
26
- /** Structural equality over JSON values. @throws {ConformanceFailure} */
27
- export const same = (actual: unknown, expected: unknown, message: string): void => {
28
- if (!sameValue(actual, expected)) {
29
- throw new ConformanceFailure(`${message}: expected ${show(expected)}, got ${show(actual)}`)
22
+ const same = (actual: unknown, expected: unknown, message: string): void => {
23
+ if (!changesHelper.sameValue(actual, expected)) {
24
+ throw new ConformanceFailure(`${message}: expected ${show(expected)}, got ${show(actual)}`)
25
+ }
30
26
  }
31
- }
32
27
 
33
- /** The same members in any order. @throws {ConformanceFailure} */
34
- export const sameSet = (actual: readonly unknown[], expected: readonly unknown[], message: string): void => {
35
- const sort = (values: readonly unknown[]) => [...values].map(show).sort()
36
- same(sort(actual), sort(expected), message)
37
- }
28
+ const sameSet = (actual: readonly unknown[], expected: readonly unknown[], message: string): void => {
29
+ const sort = (values: readonly unknown[]) => [...values].map(show).sort()
30
+ same(sort(actual), sort(expected), message)
31
+ }
38
32
 
39
- /**
40
- * The promise rejects with an error of the class (matched by instance or by its registered type
41
- * name, so a duplicate module copy counts). Answers the error.
42
- *
43
- * @throws {ConformanceFailure}
44
- */
45
- export const rejects = async (
46
- run: Promise<unknown> | (() => Promise<unknown>), expected: { typeName: string }, message: string
47
- ): Promise<Error> => {
48
- try {
49
- await (typeof run === 'function' ? run() : run)
50
- } catch (error) {
51
- const type = (error as { type?: unknown } | null)?.type
52
- if (error instanceof (expected as unknown as abstract new (...args: never[]) => unknown)
53
- || (typeof type === 'string' && type === expected.typeName)) {
54
- return error as Error
33
+ const rejects = async (
34
+ run: Promise<unknown> | (() => Promise<unknown>), expected: { typeName: string }, message: string
35
+ ): Promise<Error> => {
36
+ try {
37
+ await (typeof run === 'function' ? run() : run)
38
+ } catch (error) {
39
+ const type = (error as { type?: unknown } | null)?.type
40
+ if (error instanceof (expected as unknown as abstract new (...args: never[]) => unknown)
41
+ || (typeof type === 'string' && type === expected.typeName)) {
42
+ return error as Error
43
+ }
44
+ throw new ConformanceFailure(`${message}: expected ${expected.typeName}, got ${show((error as Error)?.message ?? error)}`)
55
45
  }
56
- throw new ConformanceFailure(`${message}: expected ${expected.typeName}, got ${show((error as Error)?.message ?? error)}`)
46
+ throw new ConformanceFailure(`${message}: expected ${expected.typeName}, nothing was thrown`)
57
47
  }
58
- throw new ConformanceFailure(`${message}: expected ${expected.typeName}, nothing was thrown`)
48
+
49
+ return { check, same, sameSet, rejects }
59
50
  }
51
+
52
+ export const assertHelper = createAssertHelper()
53
+
54
+ /** @deprecated compat:factory-refactor — use `assertHelper.check(…)` */
55
+ export const check = (condition: unknown, message: string): void => assertHelper.check(condition, message)
56
+
57
+ /** @deprecated compat:factory-refactor — use `assertHelper.same(…)` */
58
+ export const same = (actual: unknown, expected: unknown, message: string): void => assertHelper.same(actual, expected, message)
59
+
60
+ /** @deprecated compat:factory-refactor — use `assertHelper.sameSet(…)` */
61
+ export const sameSet = (actual: readonly unknown[], expected: readonly unknown[], message: string): void =>
62
+ assertHelper.sameSet(actual, expected, message)
63
+
64
+ /** @deprecated compat:factory-refactor — use `assertHelper.rejects(…)` */
65
+ export const rejects = async (
66
+ run: Promise<unknown> | (() => Promise<unknown>), expected: { typeName: string }, message: string
67
+ ): Promise<Error> => await assertHelper.rejects(run, expected, message)