@canonical/pragma-cli 0.38.0 → 0.42.0

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 (365) hide show
  1. package/README.md +3 -3
  2. package/dist/package.json +16 -16
  3. package/dist/pragma.conf.js +273 -176
  4. package/dist/pragma.conf.js.map +1 -1
  5. package/dist/src/capabilities/capabilities/capabilities.verb.js +2 -0
  6. package/dist/src/capabilities/capabilities/capabilities.verb.js.map +1 -1
  7. package/dist/src/capabilities/capabilities/catalog.js +47 -51
  8. package/dist/src/capabilities/capabilities/catalog.js.map +1 -1
  9. package/dist/src/capabilities/capabilities/index.js +2 -0
  10. package/dist/src/capabilities/capabilities/index.js.map +1 -1
  11. package/dist/src/capabilities/capabilities/types.js +5 -5
  12. package/dist/src/capabilities/colophon/colophon.render.js +7 -4
  13. package/dist/src/capabilities/colophon/colophon.render.js.map +1 -1
  14. package/dist/src/capabilities/colophon/colophon.verb.js +1 -0
  15. package/dist/src/capabilities/colophon/colophon.verb.js.map +1 -1
  16. package/dist/src/capabilities/colophon/markdownTerminal.js.map +1 -1
  17. package/dist/src/capabilities/config/get.verb.js +2 -0
  18. package/dist/src/capabilities/config/get.verb.js.map +1 -1
  19. package/dist/src/capabilities/config/runField.js +2 -2
  20. package/dist/src/capabilities/config/runField.js.map +1 -1
  21. package/dist/src/capabilities/config/set.verb.js +2 -0
  22. package/dist/src/capabilities/config/set.verb.js.map +1 -1
  23. package/dist/src/capabilities/config/show.render.js.map +1 -1
  24. package/dist/src/capabilities/config/show.verb.js +1 -0
  25. package/dist/src/capabilities/config/show.verb.js.map +1 -1
  26. package/dist/src/capabilities/config/unset.verb.js +2 -0
  27. package/dist/src/capabilities/config/unset.verb.js.map +1 -1
  28. package/dist/src/capabilities/create/constants.js +8 -5
  29. package/dist/src/capabilities/create/constants.js.map +1 -1
  30. package/dist/src/capabilities/create/create.render.js.map +1 -1
  31. package/dist/src/capabilities/create/create.verb.js +36 -7
  32. package/dist/src/capabilities/create/create.verb.js.map +1 -1
  33. package/dist/src/capabilities/create/createSurface.generated.js +22 -0
  34. package/dist/src/capabilities/create/createSurface.generated.js.map +1 -1
  35. package/dist/src/capabilities/create/index.js +1 -1
  36. package/dist/src/capabilities/create/pickGenerator.js +1 -0
  37. package/dist/src/capabilities/create/pickGenerator.js.map +1 -1
  38. package/dist/src/capabilities/distribution.js +3 -1
  39. package/dist/src/capabilities/distribution.js.map +1 -1
  40. package/dist/src/capabilities/doctor/checks/checkPackageRefs.js +10 -2
  41. package/dist/src/capabilities/doctor/checks/checkPackageRefs.js.map +1 -1
  42. package/dist/src/capabilities/doctor/checks/targetHealth.js.map +1 -1
  43. package/dist/src/capabilities/doctor/doctor.verb.js +2 -0
  44. package/dist/src/capabilities/doctor/doctor.verb.js.map +1 -1
  45. package/dist/src/capabilities/graph/connect.verb.js +2 -0
  46. package/dist/src/capabilities/graph/connect.verb.js.map +1 -1
  47. package/dist/src/capabilities/graph/inspect.render.js +7 -1
  48. package/dist/src/capabilities/graph/inspect.render.js.map +1 -1
  49. package/dist/src/capabilities/graph/inspect.verb.js +2 -0
  50. package/dist/src/capabilities/graph/inspect.verb.js.map +1 -1
  51. package/dist/src/capabilities/graph/query.render.js +37 -5
  52. package/dist/src/capabilities/graph/query.render.js.map +1 -1
  53. package/dist/src/capabilities/graph/query.verb.js +2 -0
  54. package/dist/src/capabilities/graph/query.verb.js.map +1 -1
  55. package/dist/src/capabilities/graph/queryText.js +28 -0
  56. package/dist/src/capabilities/graph/queryText.js.map +1 -1
  57. package/dist/src/capabilities/graph/runConnect.js +3 -3
  58. package/dist/src/capabilities/graph/runConnect.js.map +1 -1
  59. package/dist/src/capabilities/graph/runQuery.js +8 -6
  60. package/dist/src/capabilities/graph/runQuery.js.map +1 -1
  61. package/dist/src/capabilities/info/info.verb.js +2 -0
  62. package/dist/src/capabilities/info/info.verb.js.map +1 -1
  63. package/dist/src/capabilities/meta/mcp.verb.js +1 -0
  64. package/dist/src/capabilities/meta/mcp.verb.js.map +1 -1
  65. package/dist/src/capabilities/ontology/verbs.js +7 -10
  66. package/dist/src/capabilities/ontology/verbs.js.map +1 -1
  67. package/dist/src/capabilities/prompt/verbs.js +5 -4
  68. package/dist/src/capabilities/prompt/verbs.js.map +1 -1
  69. package/dist/src/capabilities/resources/provider.js +6 -3
  70. package/dist/src/capabilities/resources/provider.js.map +1 -1
  71. package/dist/src/capabilities/setup/operations/writability.js.map +1 -1
  72. package/dist/src/capabilities/setup/plan.render.js.map +1 -1
  73. package/dist/src/capabilities/setup/setup.verb.js +13 -5
  74. package/dist/src/capabilities/setup/setup.verb.js.map +1 -1
  75. package/dist/src/capabilities/shared/calls.js +16 -0
  76. package/dist/src/capabilities/shared/calls.js.map +1 -0
  77. package/dist/src/capabilities/shared/packageManager.js.map +1 -1
  78. package/dist/src/capabilities/skill/discover.js.map +1 -1
  79. package/dist/src/capabilities/skill/render.js +7 -5
  80. package/dist/src/capabilities/skill/render.js.map +1 -1
  81. package/dist/src/capabilities/skill/verbs.js +5 -4
  82. package/dist/src/capabilities/skill/verbs.js.map +1 -1
  83. package/dist/src/capabilities/sources/collectStatus.js +6 -1
  84. package/dist/src/capabilities/sources/collectStatus.js.map +1 -1
  85. package/dist/src/capabilities/sources/index.js +4 -3
  86. package/dist/src/capabilities/sources/index.js.map +1 -1
  87. package/dist/src/capabilities/sources/reset.render.js +37 -0
  88. package/dist/src/capabilities/sources/reset.render.js.map +1 -0
  89. package/dist/src/capabilities/sources/reset.verb.js +51 -0
  90. package/dist/src/capabilities/sources/reset.verb.js.map +1 -0
  91. package/dist/src/capabilities/sources/runReset.js +59 -0
  92. package/dist/src/capabilities/sources/runReset.js.map +1 -0
  93. package/dist/src/capabilities/sources/runUpdate.js +7 -6
  94. package/dist/src/capabilities/sources/runUpdate.js.map +1 -1
  95. package/dist/src/capabilities/sources/status.render.js +24 -8
  96. package/dist/src/capabilities/sources/status.render.js.map +1 -1
  97. package/dist/src/capabilities/sources/status.verb.js +1 -0
  98. package/dist/src/capabilities/sources/status.verb.js.map +1 -1
  99. package/dist/src/capabilities/sources/update.verb.js +2 -0
  100. package/dist/src/capabilities/sources/update.verb.js.map +1 -1
  101. package/dist/src/capabilities/upgrade/upgrade.verb.js +1 -0
  102. package/dist/src/capabilities/upgrade/upgrade.verb.js.map +1 -1
  103. package/dist/src/capabilities/version/version.verb.js +1 -0
  104. package/dist/src/capabilities/version/version.verb.js.map +1 -1
  105. package/dist/src/constants.js +4 -3
  106. package/dist/src/constants.js.map +1 -1
  107. package/dist/src/kernel/completion/complete.js.map +1 -1
  108. package/dist/src/kernel/completion/emitScripts.js.map +1 -1
  109. package/dist/src/kernel/completion/parse.js.map +1 -1
  110. package/dist/src/kernel/completion/rank.js.map +1 -1
  111. package/dist/src/kernel/completion/resolve.js.map +1 -1
  112. package/dist/src/kernel/config/readConfig.js.map +1 -1
  113. package/dist/src/kernel/error/PragmaError.js.map +1 -1
  114. package/dist/src/kernel/error/index.js +1 -1
  115. package/dist/src/kernel/error/index.js.map +1 -1
  116. package/dist/src/kernel/error/recovery.js +24 -42
  117. package/dist/src/kernel/error/recovery.js.map +1 -1
  118. package/dist/src/kernel/packs/collect.js +123 -9
  119. package/dist/src/kernel/packs/collect.js.map +1 -1
  120. package/dist/src/kernel/packs/compile.js +74 -15
  121. package/dist/src/kernel/packs/compile.js.map +1 -1
  122. package/dist/src/kernel/packs/graphql/fetchGraphqlLookup.js +3 -3
  123. package/dist/src/kernel/packs/graphql/fetchGraphqlLookup.js.map +1 -1
  124. package/dist/src/kernel/packs/iri.js +2 -4
  125. package/dist/src/kernel/packs/iri.js.map +1 -1
  126. package/dist/src/kernel/packs/renderPack.js +90 -50
  127. package/dist/src/kernel/packs/renderPack.js.map +1 -1
  128. package/dist/src/kernel/packs/resolveEntity.js +202 -90
  129. package/dist/src/kernel/packs/resolveEntity.js.map +1 -1
  130. package/dist/src/kernel/packs/runBodies.js +89 -30
  131. package/dist/src/kernel/packs/runBodies.js.map +1 -1
  132. package/dist/src/kernel/packs/schema.js +66 -12
  133. package/dist/src/kernel/packs/schema.js.map +1 -1
  134. package/dist/src/kernel/packs/sparql/buildListQuery.js +80 -13
  135. package/dist/src/kernel/packs/sparql/buildListQuery.js.map +1 -1
  136. package/dist/src/kernel/packs/sparql/buildLookupQuery.js +41 -13
  137. package/dist/src/kernel/packs/sparql/buildLookupQuery.js.map +1 -1
  138. package/dist/src/kernel/packs/sparql/filterValues.js +32 -23
  139. package/dist/src/kernel/packs/sparql/filterValues.js.map +1 -1
  140. package/dist/src/kernel/packs/sparql/runSelect.js +3 -5
  141. package/dist/src/kernel/packs/sparql/runSelect.js.map +1 -1
  142. package/dist/src/kernel/packs/storyRules.js +31 -4
  143. package/dist/src/kernel/packs/storyRules.js.map +1 -1
  144. package/dist/src/kernel/packs/tierScope.js +3 -3
  145. package/dist/src/kernel/packs/tierScope.js.map +1 -1
  146. package/dist/src/kernel/packs/types.js +4 -0
  147. package/dist/src/kernel/packs/types.js.map +1 -1
  148. package/dist/src/kernel/project/cli/buildProgram.js +4 -0
  149. package/dist/src/kernel/project/cli/buildProgram.js.map +1 -1
  150. package/dist/src/kernel/project/cli/dispatch.js +32 -8
  151. package/dist/src/kernel/project/cli/dispatch.js.map +1 -1
  152. package/dist/src/kernel/project/cli/globalFlags.js.map +1 -1
  153. package/dist/src/kernel/project/cli/suggestNames.js +44 -1
  154. package/dist/src/kernel/project/cli/suggestNames.js.map +1 -1
  155. package/dist/src/kernel/project/cli/verbHelp.js +17 -18
  156. package/dist/src/kernel/project/cli/verbHelp.js.map +1 -1
  157. package/dist/src/kernel/project/mcp/buildServer.js +11 -4
  158. package/dist/src/kernel/project/mcp/buildServer.js.map +1 -1
  159. package/dist/src/kernel/project/mcp/envelope.js +3 -2
  160. package/dist/src/kernel/project/mcp/envelope.js.map +1 -1
  161. package/dist/src/kernel/project/mcp/instructions.js +101 -31
  162. package/dist/src/kernel/project/mcp/instructions.js.map +1 -1
  163. package/dist/src/kernel/project/mcp/registerVerb.js +59 -23
  164. package/dist/src/kernel/project/mcp/registerVerb.js.map +1 -1
  165. package/dist/src/kernel/render/envelope.js.map +1 -1
  166. package/dist/src/kernel/render/renderers.js +3 -3
  167. package/dist/src/kernel/render/renderers.js.map +1 -1
  168. package/dist/src/kernel/render/turtle.js +21 -4
  169. package/dist/src/kernel/render/turtle.js.map +1 -1
  170. package/dist/src/kernel/runtime/boot.js.map +1 -1
  171. package/dist/src/kernel/runtime/graphpack/build.js +54 -1
  172. package/dist/src/kernel/runtime/graphpack/build.js.map +1 -1
  173. package/dist/src/kernel/runtime/graphpack/manifest.js +14 -0
  174. package/dist/src/kernel/runtime/graphpack/manifest.js.map +1 -1
  175. package/dist/src/kernel/runtime/graphpack/read.js +3 -5
  176. package/dist/src/kernel/runtime/graphpack/read.js.map +1 -1
  177. package/dist/src/kernel/runtime/graphpack/schemas.js +1 -0
  178. package/dist/src/kernel/runtime/graphpack/schemas.js.map +1 -1
  179. package/dist/src/kernel/runtime/loadSession.js +8 -1
  180. package/dist/src/kernel/runtime/loadSession.js.map +1 -1
  181. package/dist/src/kernel/runtime/packVersion.js +75 -0
  182. package/dist/src/kernel/runtime/packVersion.js.map +1 -0
  183. package/dist/src/kernel/runtime/readEntity.js +43 -4
  184. package/dist/src/kernel/runtime/readEntity.js.map +1 -1
  185. package/dist/src/kernel/runtime/refs/resolve.js.map +1 -1
  186. package/dist/src/kernel/runtime/resolveSources.js +61 -9
  187. package/dist/src/kernel/runtime/resolveSources.js.map +1 -1
  188. package/dist/src/kernel/runtime/storeReadiness.js +3 -2
  189. package/dist/src/kernel/runtime/storeReadiness.js.map +1 -1
  190. package/dist/src/kernel/spec/call.js +167 -0
  191. package/dist/src/kernel/spec/call.js.map +1 -0
  192. package/dist/src/kernel/spec/emitReference.js +9 -13
  193. package/dist/src/kernel/spec/emitReference.js.map +1 -1
  194. package/dist/src/kernel/spec/emitSurface.js +8 -1
  195. package/dist/src/kernel/spec/emitSurface.js.map +1 -1
  196. package/dist/src/kernel/spec/guidance.js +63 -0
  197. package/dist/src/kernel/spec/guidance.js.map +1 -0
  198. package/dist/src/kernel/spec/wireType.js +36 -0
  199. package/dist/src/kernel/spec/wireType.js.map +1 -0
  200. package/package.json +16 -16
  201. package/pragma.conf.ts +294 -177
  202. package/src/capabilities/block.parity.test.ts +1 -1
  203. package/src/capabilities/callRule.test.ts +147 -0
  204. package/src/capabilities/capabilities/capabilities.test.ts +47 -19
  205. package/src/capabilities/capabilities/capabilities.verb.ts +2 -0
  206. package/src/capabilities/capabilities/catalog.ts +60 -55
  207. package/src/capabilities/capabilities/index.ts +2 -0
  208. package/src/capabilities/capabilities/types.ts +16 -19
  209. package/src/capabilities/colophon/colophon.render.ts +9 -5
  210. package/src/capabilities/colophon/colophon.verb.ts +1 -0
  211. package/src/capabilities/config/get.verb.ts +2 -0
  212. package/src/capabilities/config/runField.ts +3 -3
  213. package/src/capabilities/config/set.verb.ts +3 -0
  214. package/src/capabilities/config/show.verb.ts +2 -0
  215. package/src/capabilities/config/unset.verb.ts +2 -0
  216. package/src/capabilities/create/constants.ts +8 -5
  217. package/src/capabilities/create/create.mcp.test.ts +173 -1
  218. package/src/capabilities/create/create.test.ts +4 -2
  219. package/src/capabilities/create/create.verb.ts +45 -3
  220. package/src/capabilities/create/createGrammar.test.ts +41 -5
  221. package/src/capabilities/create/createSurface.generated.ts +22 -0
  222. package/src/capabilities/create/crossCli.subprocess.test.ts +53 -14
  223. package/src/capabilities/create/index.ts +1 -1
  224. package/src/capabilities/create/pickGenerator.ts +1 -0
  225. package/src/capabilities/create/shippedCreate.subprocess.test.ts +28 -7
  226. package/src/capabilities/distribution.test.ts +6 -3
  227. package/src/capabilities/distribution.ts +6 -0
  228. package/src/capabilities/doctor/checks/checkPackageRefs.ts +14 -2
  229. package/src/capabilities/doctor/doctor.test.ts +38 -1
  230. package/src/capabilities/doctor/doctor.verb.ts +2 -0
  231. package/src/capabilities/emptyRecovery.test.ts +5 -0
  232. package/src/capabilities/graph/connect.verb.ts +2 -0
  233. package/src/capabilities/graph/inspect.render.ts +9 -1
  234. package/src/capabilities/graph/inspect.test.ts +73 -0
  235. package/src/capabilities/graph/inspect.verb.ts +2 -0
  236. package/src/capabilities/graph/query.render.test.ts +35 -0
  237. package/src/capabilities/graph/query.render.ts +45 -7
  238. package/src/capabilities/graph/query.shipped.exec.test.ts +44 -0
  239. package/src/capabilities/graph/query.verb.ts +2 -0
  240. package/src/capabilities/graph/queryText.test.ts +55 -1
  241. package/src/capabilities/graph/queryText.ts +47 -0
  242. package/src/capabilities/graph/runConnect.ts +4 -5
  243. package/src/capabilities/graph/runQuery.ts +23 -9
  244. package/src/capabilities/info/info.verb.ts +2 -0
  245. package/src/capabilities/links.shipped.exec.test.ts +398 -0
  246. package/src/capabilities/listBudget.shipped.exec.test.ts +2 -2
  247. package/src/capabilities/listLookup.shipped.exec.test.ts +122 -13
  248. package/src/capabilities/listQuery.shipped.exec.test.ts +39 -16
  249. package/src/capabilities/lookupBudget.shipped.exec.test.ts +208 -0
  250. package/src/capabilities/meta/mcp.verb.ts +2 -0
  251. package/src/capabilities/ontology/ontology.test.ts +24 -0
  252. package/src/capabilities/ontology/verbs.ts +16 -12
  253. package/src/capabilities/prompt/verbs.ts +10 -4
  254. package/src/capabilities/resources/provider.ts +7 -3
  255. package/src/capabilities/resources/resources.test.ts +2 -1
  256. package/src/capabilities/setup/setup.verb.ts +16 -0
  257. package/src/capabilities/shared/calls.ts +20 -0
  258. package/src/capabilities/skill/render.ts +9 -6
  259. package/src/capabilities/skill/verbs.ts +8 -4
  260. package/src/capabilities/sources/collectStatus.ts +6 -1
  261. package/src/capabilities/sources/index.ts +4 -3
  262. package/src/capabilities/sources/reset.render.test.ts +33 -0
  263. package/src/capabilities/sources/reset.render.ts +43 -0
  264. package/src/capabilities/sources/reset.verb.ts +59 -0
  265. package/src/capabilities/sources/runReset.ts +65 -0
  266. package/src/capabilities/sources/runUpdate.ts +12 -11
  267. package/src/capabilities/sources/sources.test.ts +158 -3
  268. package/src/capabilities/sources/status.render.test.ts +63 -1
  269. package/src/capabilities/sources/status.render.ts +25 -8
  270. package/src/capabilities/sources/status.verb.ts +2 -0
  271. package/src/capabilities/sources/types.ts +26 -0
  272. package/src/capabilities/sources/update.verb.ts +3 -0
  273. package/src/capabilities/surface.test.ts +29 -4
  274. package/src/capabilities/token.parity.test.ts +3 -2
  275. package/src/capabilities/tokenGraph.shipped.exec.test.ts +14 -2
  276. package/src/capabilities/upgrade/upgrade.verb.ts +1 -0
  277. package/src/capabilities/variable.parity.test.ts +2 -1
  278. package/src/capabilities/version/version.verb.ts +1 -0
  279. package/src/capabilities/wireContract.test.ts +110 -0
  280. package/src/constants.ts +4 -3
  281. package/src/kernel/completion/shellDrive.test.ts +2 -2
  282. package/src/kernel/copy.test.ts +1 -1
  283. package/src/kernel/error/PragmaError.test.ts +7 -3
  284. package/src/kernel/error/index.ts +1 -1
  285. package/src/kernel/error/recovery.test.ts +52 -18
  286. package/src/kernel/error/recovery.ts +25 -46
  287. package/src/kernel/error/types.ts +1 -1
  288. package/src/kernel/packs/collect.test.ts +79 -0
  289. package/src/kernel/packs/collect.ts +151 -11
  290. package/src/kernel/packs/compile.test.ts +107 -10
  291. package/src/kernel/packs/compile.ts +100 -11
  292. package/src/kernel/packs/filteredEmpty.test.ts +22 -5
  293. package/src/kernel/packs/graphql/fetchGraphqlLookup.ts +5 -6
  294. package/src/kernel/packs/graphqlSource.test.ts +42 -0
  295. package/src/kernel/packs/iri.ts +5 -4
  296. package/src/kernel/packs/listNotice.test.ts +1 -1
  297. package/src/kernel/packs/nounLink.test.ts +385 -0
  298. package/src/kernel/packs/renderPack.ts +137 -57
  299. package/src/kernel/packs/resolveEntity.test.ts +170 -1
  300. package/src/kernel/packs/resolveEntity.ts +281 -101
  301. package/src/kernel/packs/runBodies.ts +133 -31
  302. package/src/kernel/packs/schema.ts +77 -12
  303. package/src/kernel/packs/sparql/buildListQuery.test.ts +78 -1
  304. package/src/kernel/packs/sparql/buildListQuery.ts +106 -16
  305. package/src/kernel/packs/sparql/buildLookupQuery.ts +44 -13
  306. package/src/kernel/packs/sparql/filterValues.test.ts +36 -4
  307. package/src/kernel/packs/sparql/filterValues.ts +50 -31
  308. package/src/kernel/packs/sparql/runSelect.ts +4 -6
  309. package/src/kernel/packs/storyRules.ts +37 -4
  310. package/src/kernel/packs/tierScope.test.ts +120 -6
  311. package/src/kernel/packs/tierScope.ts +5 -7
  312. package/src/kernel/packs/toolDescription.test.ts +59 -74
  313. package/src/kernel/packs/types.ts +93 -11
  314. package/src/kernel/project/cli/buildProgram.ts +4 -0
  315. package/src/kernel/project/cli/dispatch.ts +34 -8
  316. package/src/kernel/project/cli/suggest.test.ts +77 -0
  317. package/src/kernel/project/cli/suggestNames.ts +47 -1
  318. package/src/kernel/project/cli/verbHelp.ts +16 -19
  319. package/src/kernel/project/mcp/buildServer.ts +15 -4
  320. package/src/kernel/project/mcp/envelope.ts +3 -2
  321. package/src/kernel/project/mcp/instructions.ts +123 -35
  322. package/src/kernel/project/mcp/prompts/prompts.test.ts +136 -20
  323. package/src/kernel/project/mcp/registerVerb.test.ts +28 -0
  324. package/src/kernel/project/mcp/registerVerb.ts +67 -29
  325. package/src/kernel/render/__snapshots__/renderers.test.ts.snap +1 -1
  326. package/src/kernel/render/renderers.test.ts +2 -2
  327. package/src/kernel/render/renderers.ts +3 -3
  328. package/src/kernel/render/turtle.ts +24 -4
  329. package/src/kernel/runtime/graphpack/build.ts +58 -1
  330. package/src/kernel/runtime/graphpack/graphpack.test.ts +20 -0
  331. package/src/kernel/runtime/graphpack/manifest.ts +15 -0
  332. package/src/kernel/runtime/graphpack/read.ts +4 -6
  333. package/src/kernel/runtime/graphpack/schemas.ts +1 -0
  334. package/src/kernel/runtime/graphpack/types.ts +13 -0
  335. package/src/kernel/runtime/loadSession.ts +8 -1
  336. package/src/kernel/runtime/packVersion.test.ts +61 -0
  337. package/src/kernel/runtime/packVersion.ts +84 -0
  338. package/src/kernel/runtime/readEntity.ts +61 -6
  339. package/src/kernel/runtime/resolveSources.test.ts +90 -7
  340. package/src/kernel/runtime/resolveSources.ts +91 -10
  341. package/src/kernel/runtime/storeReadiness.ts +4 -4
  342. package/src/kernel/runtime/types.ts +12 -0
  343. package/src/kernel/spec/call.test.ts +95 -0
  344. package/src/kernel/spec/call.ts +186 -0
  345. package/src/kernel/spec/emitReference.ts +9 -13
  346. package/src/kernel/spec/emitSurface.ts +9 -1
  347. package/src/kernel/spec/guidance.test.ts +49 -0
  348. package/src/kernel/spec/guidance.ts +68 -0
  349. package/src/kernel/spec/index.ts +4 -0
  350. package/src/kernel/spec/surfaceConformance.test.ts +2 -2
  351. package/src/kernel/spec/types.ts +64 -7
  352. package/src/kernel/spec/wireType.ts +53 -0
  353. package/src/testing/behavioral/README.md +1 -1
  354. package/src/testing/behavioral/errorMatrix.mcp.test.ts +2 -1
  355. package/src/testing/behavioral/journeys.firstInstall.test.ts +72 -2
  356. package/src/testing/behavioral/parity.test.ts +40 -0
  357. package/src/testing/eval/__snapshots__/eval.test.ts.snap +1 -1
  358. package/src/testing/eval/cases/assembly.ts +4 -3
  359. package/src/testing/eval/cases/stable.ts +2 -3
  360. package/src/testing/helpers/callSchema.ts +40 -0
  361. package/src/testing/helpers/parity.ts +35 -2
  362. package/src/testing/setupCallChecking.ts +30 -0
  363. package/dist/src/capabilities/capabilities/hints.js +0 -223
  364. package/dist/src/capabilities/capabilities/hints.js.map +0 -1
  365. package/src/capabilities/capabilities/hints.ts +0 -266
@@ -58,7 +58,7 @@ ex:slider a ex:Widget ; ex:name "Slider" ; ex:kind "input" .
58
58
  const EMPTY_RECOVERY = {
59
59
  message:
60
60
  "Widgets are recorded by the catalogue build, and a store built before it ran records none.",
61
- cli: "sources update",
61
+ call: { verb: "sources update" },
62
62
  } as const;
63
63
 
64
64
  const listShape = (uriClass: string) => ({
@@ -133,10 +133,12 @@ describe("a zero-row answer names the filter and keeps the recovery", () => {
133
133
  expect(outcome.exitCode).toBe(0);
134
134
  const said = `${outcome.stdout ?? ""}${outcome.stderr ?? ""}`;
135
135
  expect(said).toContain("No widget matches `--search zzzznotreal`.");
136
- // The filter is the news; the story's account of where widgets come from is
137
- // the other half, and a reader who mistyped nothing needs it.
136
+ // The filter is the news, with how to widen; the story's account of where
137
+ // widgets come from is kept — and the rebuild is not advised, because a
138
+ // filter that missed says nothing about the store.
139
+ expect(said).toContain("drop the argument, or loosen it");
138
140
  expect(said).toContain("recorded by the catalogue build");
139
- expect(said).toContain("pragma sources update");
141
+ expect(said).not.toContain("sources update");
140
142
  });
141
143
 
142
144
  it("every filter in force is named", async () => {
@@ -195,7 +197,7 @@ describe("a zero-row answer names the filter and keeps the recovery", () => {
195
197
  const said = `${outcome.stderr ?? ""}`;
196
198
  expect(said).toContain("No gadget matches `--kind input`.");
197
199
  expect(said).toContain("recorded by the catalogue build");
198
- expect(said).toContain("pragma sources update");
200
+ expect(said).not.toContain("sources update");
199
201
  });
200
202
 
201
203
  it("an EMPTY POPULATION unfiltered gets the kernel's own sentence", async () => {
@@ -210,4 +212,19 @@ describe("a zero-row answer names the filter and keeps the recovery", () => {
210
212
  expect(outcome.stderr).toContain("recorded by the catalogue build");
211
213
  expect(outcome.stderr).toContain("pragma sources update");
212
214
  });
215
+
216
+ it("a story with no recovery of its own: rebuild unfiltered, never when filtered", async () => {
217
+ const bare: PackDefinition = {
218
+ ...GADGET_PACK,
219
+ list: { ...listShape("ex:Gadget") },
220
+ };
221
+ const said = async (params: Record<string, unknown>): Promise<string> =>
222
+ (await executeVerb(listVerb(bare), params, REAL, as(PLAIN))).stderr ?? "";
223
+
224
+ expect(await said({})).toContain("pragma sources update");
225
+ const filtered = await said({ kind: "input" });
226
+ expect(filtered).toContain("drop the argument, or loosen it");
227
+ expect(filtered).not.toContain("sources update");
228
+ expect(filtered).not.toContain("needs building");
229
+ });
213
230
  });
@@ -14,7 +14,7 @@
14
14
  * composed only from validated pack terms and compiled schema names.
15
15
  */
16
16
 
17
- import { cliRecovery, PragmaError } from "../../error/index.js";
17
+ import { callRecovery, PragmaError } from "../../error/index.js";
18
18
  import type { PragmaRuntime } from "../../runtime/index.js";
19
19
  import type { PackChildRow, PackEntity, PackLookup } from "../types.js";
20
20
  import {
@@ -56,10 +56,9 @@ export async function fetchGraphqlLookup(
56
56
  .join("; ");
57
57
  throw PragmaError.storeUnavailable(`GraphQL lookup failed: ${messages}`, {
58
58
  // `graph inspect` needs a <uri>; `ontology list` is runnable as-is.
59
- recovery: cliRecovery(
60
- "ontology list",
59
+ recovery: callRecovery(
60
+ { verb: "ontology list" },
61
61
  "Check the loaded ontology and namespaces.",
62
- { tool: "ontology_list" },
63
62
  ),
64
63
  });
65
64
  }
@@ -72,8 +71,8 @@ export async function fetchGraphqlLookup(
72
71
  throw new PragmaError({
73
72
  code: "ENTITY_NOT_FOUND",
74
73
  message: `Cannot resolve <${entityUri}> — its rdf:type may be missing from the ontology.`,
75
- recovery: cliRecovery(
76
- `graph inspect ${entityUri}`,
74
+ recovery: callRecovery(
75
+ { verb: "graph inspect", params: { uri: entityUri } },
77
76
  "Inspect the entity's triples to check its rdf:type.",
78
77
  ),
79
78
  });
@@ -14,6 +14,7 @@ import { buildFixtureRuntime } from "../../testing/helpers/packRuntime.js";
14
14
  import type { PragmaRuntime, StoreSession } from "../runtime/types.js";
15
15
  import { compilePack } from "./compile.js";
16
16
  import { buildLookupDocument } from "./graphql/buildLookupDocument.js";
17
+ import { fetchGraphqlLookup } from "./graphql/fetchGraphqlLookup.js";
17
18
  import type { LookupOutput } from "./resolveEntity.js";
18
19
  import { parsePackDefinition } from "./schema.js";
19
20
  import type { PackDefinition, PackLookup } from "./types.js";
@@ -320,6 +321,47 @@ describe("GraphQL engine — against the compiled fixture schema (PROTECTED)", (
320
321
 
321
322
  const lookup = GBLOCK.lookup as PackLookup;
322
323
 
324
+ /** The fixture runtime with its GraphQL executor replaced by a canned answer. */
325
+ const answering = (answer: unknown): PragmaRuntime =>
326
+ ({
327
+ ...rt,
328
+ query: { ...rt.query, graphql: async () => answer },
329
+ }) as PragmaRuntime;
330
+ const fetchWith = (answer: unknown) =>
331
+ fetchGraphqlLookup(
332
+ answering(answer),
333
+ lookup,
334
+ "https://ds.canonical.com/x",
335
+ "X",
336
+ "t",
337
+ BLOCK_PREFIXES,
338
+ );
339
+
340
+ it("an execution error recovers to the namespace list, in both spellings", async () => {
341
+ await expect(
342
+ fetchWith({ errors: [{ message: "boom" }] }),
343
+ ).rejects.toMatchObject({
344
+ code: "STORE_UNAVAILABLE",
345
+ recovery: {
346
+ cli: "pragma ontology list",
347
+ mcp: { tool: "ontology_list", params: {} },
348
+ },
349
+ });
350
+ });
351
+
352
+ it("a resolved IRI with no typed node recovers to inspecting THAT entity", async () => {
353
+ await expect(fetchWith({ data: { node: null } })).rejects.toMatchObject({
354
+ code: "ENTITY_NOT_FOUND",
355
+ recovery: {
356
+ cli: "pragma graph inspect https://ds.canonical.com/x",
357
+ mcp: {
358
+ tool: "graph_inspect",
359
+ params: { uri: "https://ds.canonical.com/x" },
360
+ },
361
+ },
362
+ });
363
+ });
364
+
323
365
  it("generates one document with derived names, Relay envelopes, subtype scoping", () => {
324
366
  const plan = buildLookupDocument(lookup, schema, "t");
325
367
  expect(plan.source).toContain("query PackLookup($uri: ID!)");
@@ -7,7 +7,7 @@
7
7
  * before it can be interpolated into a generated `<iri>` query token.
8
8
  */
9
9
 
10
- import { cliRecovery, PragmaError } from "../error/index.js";
10
+ import { callRecovery, PragmaError } from "../error/index.js";
11
11
 
12
12
  /** Characters not allowed inside a SPARQL `<IRI>`. */
13
13
  const UNSAFE_IRI_PATTERN = /[<>"{}|\\^`\s]/;
@@ -86,9 +86,10 @@ function tryResolveUri(
86
86
 
87
87
  return PragmaError.invalidInput("prefix", prefix, {
88
88
  validOptions: Object.keys(prefixes),
89
- recovery: cliRecovery("ontology list", "List known ontology prefixes.", {
90
- tool: "ontology_list",
91
- }),
89
+ recovery: callRecovery(
90
+ { verb: "ontology list" },
91
+ "List known ontology prefixes.",
92
+ ),
92
93
  });
93
94
  }
94
95
 
@@ -46,7 +46,7 @@ ex:slider a ex:Widget ; ex:name "Slider" ; ex:kind "input" .
46
46
  /** The recovery a story authors for an EMPTY POPULATION — never for a filter. */
47
47
  const EMPTY_RECOVERY = {
48
48
  message: "No widgets in the store.",
49
- cli: "sources update",
49
+ call: { verb: "sources update" },
50
50
  } as const;
51
51
 
52
52
  const WIDGET_LIST = {
@@ -0,0 +1,385 @@
1
+ /**
2
+ * A filter that names a noun accepts what that noun's lookup accepts, and a
3
+ * `many` expand field adds values to a row, never rows.
4
+ */
5
+
6
+ import { beforeAll, describe, expect, it } from "vitest";
7
+ import { buildFixtureRuntime } from "../../testing/helpers/packRuntime.js";
8
+ import type { ConfigLayers } from "../config/index.js";
9
+ import type { PragmaRuntime } from "../runtime/types.js";
10
+ import type { CapabilityModule, VerbSpec } from "../spec/types.js";
11
+ import { assembleEffectiveModules, screenPackageStories } from "./collect.js";
12
+ import { compilePack, compileStoryModule } from "./compile.js";
13
+ import type { LookupOutput } from "./resolveEntity.js";
14
+ import { parsePackDefinition } from "./schema.js";
15
+ import {
16
+ distributionSource,
17
+ type PackDefinition,
18
+ type PackPage,
19
+ } from "./types.js";
20
+ import { verbKey } from "./uniqueness.js";
21
+
22
+ const PREFIXES = {
23
+ ex: "https://example.org/shop#",
24
+ owl: "http://www.w3.org/2002/07/owl#",
25
+ };
26
+
27
+ const TTL = `
28
+ @prefix ex: <https://example.org/shop#> .
29
+ @prefix owl: <http://www.w3.org/2002/07/owl#> .
30
+
31
+ ex:Maker a owl:Class .
32
+ ex:Widget a owl:Class .
33
+
34
+ ex:acme a ex:Maker ; ex:name "Acme" ; ex:code "A1" .
35
+ ex:acme2 a ex:Maker ; ex:name "Acme" .
36
+ ex:bolt a ex:Maker ; ex:name "Bolt" .
37
+ ex:idle a ex:Maker ; ex:name "Idle" .
38
+
39
+ ex:button a ex:Widget ; ex:name "Button" ; ex:madeBy ex:acme ;
40
+ ex:alias "btn" , "Btn" , "push-button" .
41
+ ex:label a ex:Widget ; ex:name "Label" ; ex:madeBy ex:acme2 .
42
+ ex:slider a ex:Widget ; ex:name "Slider" ; ex:madeBy ex:bolt .
43
+
44
+ ex:bolt ex:part
45
+ [ ex:group "g" ; ex:label "f" ; ex:tag "x" , "y" ] ,
46
+ [ ex:group "g" ; ex:label "c" ; ex:tag "x" ] ,
47
+ [ ex:group "g" ; ex:label "e" ] ,
48
+ [ ex:group "g" ; ex:label "a" ; ex:tag "y" ] ,
49
+ [ ex:group "g" ; ex:label "d" ; ex:tag "x" ] ,
50
+ [ ex:group "g" ; ex:label "b" ] .
51
+ `;
52
+
53
+ const MAKER: PackDefinition = {
54
+ noun: "maker",
55
+ lookup: {
56
+ by: "ex:name",
57
+ type: "ex:Maker",
58
+ expand: [
59
+ {
60
+ name: "widgets",
61
+ kind: "table",
62
+ relation: "^ex:madeBy",
63
+ select: [
64
+ { name: "name", property: "ex:name", noun: "widget" },
65
+ { name: "aliases", property: "ex:alias", many: true },
66
+ ],
67
+ },
68
+ {
69
+ name: "parts",
70
+ kind: "table",
71
+ relation: "ex:part",
72
+ // Every part ties on the one declared key.
73
+ orderBy: ["group"],
74
+ select: [
75
+ { name: "group", property: "ex:group" },
76
+ { name: "label", property: "ex:label" },
77
+ { name: "tags", property: "ex:tag", many: true },
78
+ ],
79
+ },
80
+ ],
81
+ },
82
+ };
83
+
84
+ const WIDGET: PackDefinition = {
85
+ noun: "widget",
86
+ list: {
87
+ query: [
88
+ "SELECT ?uri ?name ?maker ?makerUri WHERE {",
89
+ " ?uri a ex:Widget ; ex:name ?name ; ex:madeBy ?makerUri .",
90
+ " ?makerUri ex:name ?maker .",
91
+ "} ORDER BY ?name",
92
+ ].join("\n"),
93
+ columns: [
94
+ { field: "name", label: "Name" },
95
+ { field: "maker", label: "Maker", noun: "maker" },
96
+ ],
97
+ filters: [
98
+ { param: "maker", variable: "maker", noun: "maker", entity: "makerUri" },
99
+ ],
100
+ },
101
+ lookup: { by: "ex:name", type: "ex:Widget" },
102
+ };
103
+
104
+ const STORIES = new Map([MAKER, WIDGET].map((story) => [story.noun, story]));
105
+
106
+ const verb = (story: PackDefinition, name: string): VerbSpec =>
107
+ compilePack(
108
+ story,
109
+ distributionSource("test:links"),
110
+ PREFIXES,
111
+ (noun) => STORIES.get(noun)?.lookup,
112
+ ).find((v) => verbKey(v.path) === `${story.noun} ${name}`) as VerbSpec;
113
+
114
+ let rt: PragmaRuntime;
115
+ beforeAll(async () => {
116
+ ({ rt } = await buildFixtureRuntime({ ttl: TTL, prefixes: PREFIXES }));
117
+ });
118
+
119
+ const widgetsBy = async (maker: unknown): Promise<PackPage["rows"]> =>
120
+ ((await verb(WIDGET, "list").run({ maker }, rt)) as PackPage).rows;
121
+
122
+ describe("a filter that names a noun", () => {
123
+ it("takes a name, and a shared name means every entity it reaches", async () => {
124
+ const rows = await widgetsBy("acme");
125
+ expect(rows.map((row) => row.name)).toEqual(["Button", "Label"]);
126
+ });
127
+
128
+ it("takes a prefixed IRI and an absolute IRI, which reach one entity", async () => {
129
+ expect((await widgetsBy("ex:acme2")).map((row) => row.name)).toEqual([
130
+ "Label",
131
+ ]);
132
+ expect(
133
+ (await widgetsBy("https://example.org/shop#bolt")).map((row) => row.name),
134
+ ).toEqual(["Slider"]);
135
+ });
136
+
137
+ it("takes several values as a union", async () => {
138
+ const rows = await widgetsBy(["Bolt", "ex:acme"]);
139
+ expect(rows.map((row) => row.name)).toEqual(["Button", "Slider"]);
140
+ });
141
+
142
+ it("leaves the entity variable out of the rows", async () => {
143
+ expect(Object.keys((await widgetsBy("Bolt"))[0] ?? {})).toEqual([
144
+ "uri",
145
+ "name",
146
+ "maker",
147
+ ]);
148
+ });
149
+
150
+ it("answers a calm empty list for an entity that exists and no row names", async () => {
151
+ expect(await widgetsBy("Idle")).toEqual([]);
152
+ });
153
+
154
+ it("refuses a value the noun's lookup cannot resolve, offering what a lookup miss offers", async () => {
155
+ await expect(widgetsBy(["Bolt", "Bolts"])).rejects.toMatchObject({
156
+ code: "INVALID_INPUT",
157
+ message: 'No maker is named "Bolts".',
158
+ suggestions: ["Bolt"],
159
+ });
160
+ });
161
+
162
+ it("refuses an empty value as empty input", async () => {
163
+ await expect(widgetsBy("")).rejects.toMatchObject({
164
+ code: "INVALID_INPUT",
165
+ message: 'Invalid maker "(empty)".',
166
+ });
167
+ });
168
+
169
+ it("is a configuration error when no story declares the noun", async () => {
170
+ const orphan = compilePack(
171
+ WIDGET,
172
+ distributionSource("test:links"),
173
+ PREFIXES,
174
+ )[0] as VerbSpec;
175
+ await expect(orphan.run({ maker: "Acme" }, rt)).rejects.toMatchObject({
176
+ code: "CONFIG_ERROR",
177
+ });
178
+ });
179
+ });
180
+
181
+ describe("a noun filter across the shipped and the project's stories", () => {
182
+ const shipped = (...stories: PackDefinition[]): CapabilityModule[] =>
183
+ stories.map((story) =>
184
+ compileStoryModule(
185
+ story,
186
+ distributionSource("test:shipped"),
187
+ PREFIXES,
188
+ (noun) => STORIES.get(noun)?.lookup,
189
+ ),
190
+ );
191
+ const project = (stories: unknown[]): ConfigLayers =>
192
+ ({
193
+ config: { stories, prefixes: PREFIXES },
194
+ origins: { packs: "default", stories: "project" },
195
+ }) as unknown as ConfigLayers;
196
+ const makerFilter = (modules: readonly CapabilityModule[], maker: string) =>
197
+ (
198
+ modules
199
+ .flatMap((module) => module.verbs)
200
+ .find((v) => verbKey(v.path) === "widget list") as VerbSpec
201
+ ).run({ maker: [maker] }, rt) as Promise<PackPage>;
202
+
203
+ it("a project story resolves its filter through a shipped noun", async () => {
204
+ const modules = assembleEffectiveModules(shipped(MAKER), project([WIDGET]));
205
+ const page = await makerFilter(modules, "Bolt");
206
+ expect(page.rows.map((row) => row.name)).toEqual(["Slider"]);
207
+ });
208
+
209
+ it("a shipped filter resolves through the project's override of the noun it names", async () => {
210
+ const byCode = {
211
+ noun: "maker",
212
+ lookup: { by: "ex:code", type: "ex:Maker" },
213
+ };
214
+ const modules = assembleEffectiveModules(
215
+ shipped(MAKER, WIDGET),
216
+ project([byCode]),
217
+ );
218
+ const page = await makerFilter(modules, "A1");
219
+ expect(page.rows.map((row) => row.name)).toEqual(["Button"]);
220
+ await expect(makerFilter(modules, "Acme")).rejects.toMatchObject({
221
+ code: "INVALID_INPUT",
222
+ });
223
+ });
224
+
225
+ it("refuses a story naming a noun no story declares, where it is assembled", () => {
226
+ expect(() => assembleEffectiveModules([], project([WIDGET]))).toThrow(
227
+ /Invalid story in config: "widget" names the noun "maker"/,
228
+ );
229
+ });
230
+
231
+ it("a project overriding a named noun with no lookup breaks only the filter, when used", async () => {
232
+ const listOnly = { noun: "maker", list: WIDGET.list };
233
+ const modules = assembleEffectiveModules(
234
+ shipped(MAKER, WIDGET),
235
+ project([listOnly]),
236
+ );
237
+ const widgets = modules
238
+ .flatMap((module) => module.verbs)
239
+ .find((v) => verbKey(v.path) === "widget list") as VerbSpec;
240
+ expect(((await widgets.run({}, rt)) as PackPage).rows).toHaveLength(3);
241
+ await expect(makerFilter(modules, "Bolt")).rejects.toMatchObject({
242
+ code: "CONFIG_ERROR",
243
+ message: expect.stringMatching(
244
+ /The "maker" story in config declares no lookup/,
245
+ ),
246
+ });
247
+ });
248
+
249
+ it("a package story is screened against the assembled nouns, whatever the order", () => {
250
+ const entry = { source: "pkg/stories/widget.json", definition: WIDGET };
251
+ const withMaker = screenPackageStories(
252
+ assembleEffectiveModules([], project([MAKER]), [entry]),
253
+ );
254
+ expect(withMaker.modules.map((module) => module.name)).toContain("widget");
255
+ expect(withMaker.problems).toEqual([]);
256
+
257
+ const other = { noun: "gadget", lookup: MAKER.lookup };
258
+ const without = screenPackageStories(
259
+ assembleEffectiveModules([], project([other]), [entry]),
260
+ );
261
+ expect(without.modules.map((module) => module.name)).toEqual(["gadget"]);
262
+ expect(without.problems).toEqual([
263
+ {
264
+ source: "pkg/stories/widget.json",
265
+ message: 'it names the noun "maker", and no story declares it.',
266
+ },
267
+ ]);
268
+ });
269
+ });
270
+
271
+ describe("a many expand field", () => {
272
+ it("is one cell per child holding every value, sorted", async () => {
273
+ const output = (await verb(MAKER, "lookup").run(
274
+ { name: ["ex:acme"] },
275
+ rt,
276
+ )) as LookupOutput;
277
+ const widgets = output.results[0]?.widgets as { aliases?: string }[];
278
+ expect(widgets).toHaveLength(1);
279
+ expect(widgets[0]?.aliases).toBe("Btn btn push-button");
280
+ });
281
+ });
282
+
283
+ describe("a grouped expand", () => {
284
+ it("orders rows that tie on orderBy the same way on every store", async () => {
285
+ const labels: string[] = [];
286
+ for (let boot = 0; boot < 3; boot += 1) {
287
+ const fresh = await buildFixtureRuntime({ ttl: TTL, prefixes: PREFIXES });
288
+ const output = (await verb(MAKER, "lookup").run(
289
+ { name: ["Bolt"] },
290
+ fresh.rt,
291
+ )) as LookupOutput;
292
+ const parts = output.results[0]?.parts as { label: string }[];
293
+ labels.push(parts.map((part) => part.label).join(""));
294
+ }
295
+ expect(labels).toEqual(["abcdef", "abcdef", "abcdef"]);
296
+ });
297
+ });
298
+
299
+ describe("the grammar", () => {
300
+ const withFilter = (filter: Record<string, unknown>): unknown => ({
301
+ ...WIDGET,
302
+ list: { ...WIDGET.list, filters: [filter] },
303
+ });
304
+
305
+ it("accepts both stories as declared", () => {
306
+ expect(() => parsePackDefinition(MAKER, "test")).not.toThrow();
307
+ expect(() => parsePackDefinition(WIDGET, "test")).not.toThrow();
308
+ });
309
+
310
+ it.each([
311
+ [
312
+ "a noun filter with no entity",
313
+ { param: "maker", variable: "maker", noun: "maker" },
314
+ /must declare the "entity"/,
315
+ ],
316
+ [
317
+ "a noun filter with a vocabulary of its own",
318
+ {
319
+ param: "maker",
320
+ variable: "maker",
321
+ noun: "maker",
322
+ entity: "makerUri",
323
+ values: ["Acme"],
324
+ },
325
+ /mutually exclusive/,
326
+ ],
327
+ [
328
+ "an entity the query does not project",
329
+ { param: "maker", variable: "maker", noun: "maker", entity: "absent" },
330
+ /"absent" is filtered or searched but not projected/,
331
+ ],
332
+ [
333
+ "an entity without a noun",
334
+ {
335
+ param: "maker",
336
+ variable: "maker",
337
+ values: ["Acme"],
338
+ entity: "makerUri",
339
+ },
340
+ /belong to a filter that names a "noun"/,
341
+ ],
342
+ ])("refuses %s", (_, filter, reason) => {
343
+ expect(() => parsePackDefinition(withFilter(filter), "test")).toThrow(
344
+ reason,
345
+ );
346
+ });
347
+
348
+ it.each([
349
+ [
350
+ "many with blankWhenSelf",
351
+ { many: true, blankWhenSelf: true },
352
+ {},
353
+ /mutually exclusive/,
354
+ ],
355
+ [
356
+ "many on the GraphQL lane",
357
+ { many: true },
358
+ { source: "graphql" },
359
+ /sets "many", which only the SPARQL lane/,
360
+ ],
361
+ [
362
+ "ordering by a many field",
363
+ { many: true },
364
+ { orderBy: ["aliases"] },
365
+ /a "many" field/,
366
+ ],
367
+ ])("refuses %s", (_, field, expand, reason) => {
368
+ const lookup = MAKER.lookup as NonNullable<PackDefinition["lookup"]>;
369
+ const story = {
370
+ ...MAKER,
371
+ lookup: {
372
+ ...lookup,
373
+ ...("source" in expand ? { source: "graphql" } : {}),
374
+ expand: [
375
+ {
376
+ ...lookup.expand?.[0],
377
+ ...("orderBy" in expand ? expand : {}),
378
+ select: [{ name: "aliases", property: "ex:alias", ...field }],
379
+ },
380
+ ],
381
+ },
382
+ };
383
+ expect(() => parsePackDefinition(story, "test")).toThrow(reason);
384
+ });
385
+ });